mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-23 22:39:43 -05:00
Fix detection of hardware keys in keepassxc-cli
* Split calls to finding hardware keys into sync and async methods. This has the side effect of simplifying the code. * Check for keys before performing challenge/response if no keys have been found previously. * Correct timeout of user interaction message to interact with the hardware key. * Correct error in TestCli::testYubiKeyOption
This commit is contained in:
parent
7d7c635423
commit
48a3fd8e3c
@ -7233,10 +7233,6 @@ Please consider generating a new key file.</source>
|
||||
<source>Invalid YubiKey serial %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please present or touch your YubiKey to continue…</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter password to encrypt database (optional): </source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -7760,6 +7756,10 @@ Kernel: %3 %4</source>
|
||||
<source>Failed to sign challenge using Windows Hello.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please present or touch your YubiKey to continue.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtIOCompressor</name>
|
||||
|
@ -167,14 +167,14 @@ namespace Utils
|
||||
}
|
||||
}
|
||||
|
||||
auto conn = QObject::connect(YubiKey::instance(), &YubiKey::userInteractionRequest, [&] {
|
||||
err << QObject::tr("Please present or touch your YubiKey to continue…") << "\n\n" << flush;
|
||||
QObject::connect(YubiKey::instance(), &YubiKey::userInteractionRequest, [&] {
|
||||
err << QObject::tr("Please present or touch your YubiKey to continue.") << "\n\n" << flush;
|
||||
});
|
||||
|
||||
auto key = QSharedPointer<ChallengeResponseKey>(new ChallengeResponseKey({serial, slot}));
|
||||
compositeKey->addChallengeResponseKey(key);
|
||||
|
||||
QObject::disconnect(conn);
|
||||
YubiKey::instance()->findValidKeys();
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(yubiKeySlot);
|
||||
|
@ -456,7 +456,7 @@ void DatabaseOpenWidget::pollHardwareKey()
|
||||
m_ui->hardwareKeyProgress->setVisible(true);
|
||||
m_pollingHardwareKey = true;
|
||||
|
||||
YubiKey::instance()->findValidKeys();
|
||||
YubiKey::instance()->findValidKeysAsync();
|
||||
}
|
||||
|
||||
void DatabaseOpenWidget::hardwareKeyResponse(bool found)
|
||||
|
@ -122,7 +122,7 @@ void YubiKeyEditWidget::pollYubikey()
|
||||
m_compUi->comboChallengeResponse->setEnabled(false);
|
||||
m_compUi->yubikeyProgress->setVisible(true);
|
||||
|
||||
YubiKey::instance()->findValidKeys();
|
||||
YubiKey::instance()->findValidKeysAsync();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "YubiKeyInterfacePCSC.h"
|
||||
#include "YubiKeyInterfaceUSB.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
YubiKey::YubiKey()
|
||||
: m_interfaces_detect_mutex(QMutex::Recursive)
|
||||
{
|
||||
@ -27,45 +29,27 @@ YubiKey::YubiKey()
|
||||
|
||||
if (YubiKeyInterfaceUSB::instance()->isInitialized()) {
|
||||
++num_interfaces;
|
||||
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
|
||||
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));
|
||||
} else {
|
||||
qDebug("YubiKey: USB interface is not initialized.");
|
||||
}
|
||||
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
|
||||
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));
|
||||
|
||||
if (YubiKeyInterfacePCSC::instance()->isInitialized()) {
|
||||
++num_interfaces;
|
||||
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
|
||||
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));
|
||||
} else {
|
||||
qDebug("YubiKey: PCSC interface is disabled or not initialized.");
|
||||
}
|
||||
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
|
||||
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));
|
||||
|
||||
// Collapse the detectComplete signals from all interfaces into one signal
|
||||
// If multiple interfaces are used, wait for them all to finish
|
||||
auto detect_handler = [this, num_interfaces](bool found) {
|
||||
if (!m_interfaces_detect_mutex.tryLock(1000)) {
|
||||
return;
|
||||
}
|
||||
m_interfaces_detect_found |= found;
|
||||
m_interfaces_detect_completed++;
|
||||
if (m_interfaces_detect_completed != -1 && m_interfaces_detect_completed == num_interfaces) {
|
||||
m_interfaces_detect_completed = -1;
|
||||
emit detectComplete(m_interfaces_detect_found);
|
||||
}
|
||||
m_interfaces_detect_mutex.unlock();
|
||||
};
|
||||
connect(YubiKeyInterfaceUSB::instance(), &YubiKeyInterfaceUSB::detectComplete, this, detect_handler);
|
||||
connect(YubiKeyInterfacePCSC::instance(), &YubiKeyInterfacePCSC::detectComplete, this, detect_handler);
|
||||
m_initialized = num_interfaces > 0;
|
||||
|
||||
if (num_interfaces != 0) {
|
||||
m_initialized = true;
|
||||
// clang-format off
|
||||
connect(&m_interactionTimer, SIGNAL(timeout()), this, SIGNAL(userInteractionRequest()));
|
||||
connect(this, &YubiKey::challengeStarted, this, [this] { m_interactionTimer.start(); });
|
||||
connect(this, &YubiKey::challengeCompleted, this, [this] { m_interactionTimer.stop(); });
|
||||
// clang-format on
|
||||
}
|
||||
m_interactionTimer.setSingleShot(true);
|
||||
m_interactionTimer.setInterval(200);
|
||||
connect(&m_interactionTimer, SIGNAL(timeout()), this, SIGNAL(userInteractionRequest()));
|
||||
connect(this, &YubiKey::challengeStarted, this, [this] { m_interactionTimer.start(); });
|
||||
connect(this, &YubiKey::challengeCompleted, this, [this] { m_interactionTimer.stop(); });
|
||||
}
|
||||
|
||||
YubiKey* YubiKey::m_instance(nullptr);
|
||||
@ -84,12 +68,23 @@ bool YubiKey::isInitialized()
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void YubiKey::findValidKeys()
|
||||
bool YubiKey::findValidKeys()
|
||||
{
|
||||
m_interfaces_detect_completed = 0;
|
||||
m_interfaces_detect_found = false;
|
||||
YubiKeyInterfaceUSB::instance()->findValidKeys();
|
||||
YubiKeyInterfacePCSC::instance()->findValidKeys();
|
||||
bool found = false;
|
||||
if (m_interfaces_detect_mutex.tryLock(1000)) {
|
||||
found |= YubiKeyInterfaceUSB::instance()->findValidKeys();
|
||||
found |= YubiKeyInterfacePCSC::instance()->findValidKeys();
|
||||
m_interfaces_detect_mutex.unlock();
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void YubiKey::findValidKeysAsync()
|
||||
{
|
||||
QtConcurrent::run([this] {
|
||||
bool found = findValidKeys();
|
||||
emit detectComplete(found);
|
||||
});
|
||||
}
|
||||
|
||||
QList<YubiKeySlot> YubiKey::foundKeys()
|
||||
@ -207,6 +202,11 @@ YubiKey::challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_
|
||||
{
|
||||
m_error.clear();
|
||||
|
||||
// Make sure we tried to find available keys
|
||||
if (foundKeys().isEmpty()) {
|
||||
findValidKeys();
|
||||
}
|
||||
|
||||
if (YubiKeyInterfaceUSB::instance()->hasFoundKey(slot)) {
|
||||
return YubiKeyInterfaceUSB::instance()->challenge(slot, challenge, response);
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ public:
|
||||
static YubiKey* instance();
|
||||
bool isInitialized();
|
||||
|
||||
void findValidKeys();
|
||||
bool findValidKeys();
|
||||
void findValidKeysAsync();
|
||||
|
||||
QList<YubiKeySlot> foundKeys();
|
||||
QString getDisplayName(YubiKeySlot slot);
|
||||
@ -84,8 +85,6 @@ private:
|
||||
QTimer m_interactionTimer;
|
||||
bool m_initialized = false;
|
||||
QString m_error;
|
||||
int m_interfaces_detect_completed = -1;
|
||||
bool m_interfaces_detect_found = false;
|
||||
QMutex m_interfaces_detect_mutex;
|
||||
|
||||
Q_DISABLE_COPY(YubiKey)
|
||||
|
@ -37,6 +37,11 @@ QMultiMap<unsigned int, QPair<int, QString>> YubiKeyInterface::foundKeys()
|
||||
|
||||
bool YubiKeyInterface::hasFoundKey(YubiKeySlot slot)
|
||||
{
|
||||
// A serial number of 0 implies use the first key
|
||||
if (slot.first == 0 && !m_foundKeys.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& key : m_foundKeys.values(slot.first)) {
|
||||
if (slot.second == key.first) {
|
||||
return true;
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
bool hasFoundKey(YubiKeySlot slot);
|
||||
QString getDisplayName(YubiKeySlot slot);
|
||||
|
||||
virtual void findValidKeys() = 0;
|
||||
virtual bool findValidKeys() = 0;
|
||||
virtual YubiKey::ChallengeResult
|
||||
challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_vector<char>& response) = 0;
|
||||
virtual bool testChallenge(YubiKeySlot slot, bool* wouldBlock) = 0;
|
||||
|
@ -17,10 +17,9 @@
|
||||
|
||||
#include "YubiKeyInterfacePCSC.h"
|
||||
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Random.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
// MSYS2 does not define these macros
|
||||
// So set them to the value used by pcsc-lite
|
||||
#ifndef MAX_ATR_SIZE
|
||||
@ -530,109 +529,98 @@ YubiKeyInterfacePCSC* YubiKeyInterfacePCSC::instance()
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void YubiKeyInterfacePCSC::findValidKeys()
|
||||
bool YubiKeyInterfacePCSC::findValidKeys()
|
||||
{
|
||||
m_error.clear();
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// Remove all known keys
|
||||
m_foundKeys.clear();
|
||||
|
||||
QtConcurrent::run([this] {
|
||||
// This mutex protects the smartcard against concurrent transmissions
|
||||
if (!m_mutex.tryLock(1000)) {
|
||||
emit detectComplete(false);
|
||||
return;
|
||||
// Connect to each reader and look for cards
|
||||
auto readers_list = getReaders(m_sc_context);
|
||||
foreach (const QString& reader_name, readers_list) {
|
||||
|
||||
/* Some Yubikeys present their PCSC interface via USB as well
|
||||
Although this would not be a problem in itself,
|
||||
we filter these connections because in USB mode,
|
||||
the PCSC challenge-response interface is usually locked
|
||||
Instead, the other USB (HID) interface should pick up and
|
||||
interface the key.
|
||||
For more info see the comment block further below. */
|
||||
if (reader_name.contains("yubikey", Qt::CaseInsensitive)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove all known keys
|
||||
m_foundKeys.clear();
|
||||
SCARDHANDLE hCard;
|
||||
SCUINT dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
|
||||
auto rv = SCardConnect(m_sc_context,
|
||||
reader_name.toStdString().c_str(),
|
||||
SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
||||
&hCard,
|
||||
&dwActiveProtocol);
|
||||
|
||||
// Connect to each reader and look for cards
|
||||
auto readers_list = getReaders(m_sc_context);
|
||||
foreach (const QString& reader_name, readers_list) {
|
||||
if (rv == SCARD_S_SUCCESS) {
|
||||
// Read the potocol and the ATR record
|
||||
char pbReader[MAX_READERNAME] = {0};
|
||||
SCUINT dwReaderLen = sizeof(pbReader);
|
||||
SCUINT dwState = 0;
|
||||
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
||||
uint8_t pbAtr[MAX_ATR_SIZE] = {0};
|
||||
SCUINT dwAtrLen = sizeof(pbAtr);
|
||||
|
||||
/* Some Yubikeys present their PCSC interface via USB as well
|
||||
Although this would not be a problem in itself,
|
||||
we filter these connections because in USB mode,
|
||||
the PCSC challenge-response interface is usually locked
|
||||
Instead, the other USB (HID) interface should pick up and
|
||||
interface the key.
|
||||
For more info see the comment block further below. */
|
||||
if (reader_name.contains("yubikey", Qt::CaseInsensitive)) {
|
||||
continue;
|
||||
}
|
||||
rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
||||
if (rv == SCARD_S_SUCCESS && (dwProt == SCARD_PROTOCOL_T0 || dwProt == SCARD_PROTOCOL_T1)) {
|
||||
// Find which AID to use
|
||||
SCardAID satr;
|
||||
if (findAID(hCard, m_aid_codes, satr)) {
|
||||
// Build the UI name using the display name found in the ATR map
|
||||
QByteArray atr(reinterpret_cast<char*>(pbAtr), dwAtrLen);
|
||||
QString name("Unknown Key");
|
||||
if (m_atr_names.contains(atr)) {
|
||||
name = m_atr_names.value(atr);
|
||||
}
|
||||
// Add the firmware version and the serial number
|
||||
uint8_t version[3] = {0};
|
||||
getStatus(satr, version);
|
||||
name +=
|
||||
QString(" v%1.%2.%3")
|
||||
.arg(QString::number(version[0]), QString::number(version[1]), QString::number(version[2]));
|
||||
|
||||
SCARDHANDLE hCard;
|
||||
SCUINT dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
|
||||
auto rv = SCardConnect(m_sc_context,
|
||||
reader_name.toStdString().c_str(),
|
||||
SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
||||
&hCard,
|
||||
&dwActiveProtocol);
|
||||
unsigned int serial = 0;
|
||||
getSerial(satr, serial);
|
||||
|
||||
if (rv == SCARD_S_SUCCESS) {
|
||||
// Read the potocol and the ATR record
|
||||
char pbReader[MAX_READERNAME] = {0};
|
||||
SCUINT dwReaderLen = sizeof(pbReader);
|
||||
SCUINT dwState = 0;
|
||||
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
||||
uint8_t pbAtr[MAX_ATR_SIZE] = {0};
|
||||
SCUINT dwAtrLen = sizeof(pbAtr);
|
||||
|
||||
rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
||||
if (rv == SCARD_S_SUCCESS && (dwProt == SCARD_PROTOCOL_T0 || dwProt == SCARD_PROTOCOL_T1)) {
|
||||
// Find which AID to use
|
||||
SCardAID satr;
|
||||
if (findAID(hCard, m_aid_codes, satr)) {
|
||||
// Build the UI name using the display name found in the ATR map
|
||||
QByteArray atr(reinterpret_cast<char*>(pbAtr), dwAtrLen);
|
||||
QString name("Unknown Key");
|
||||
if (m_atr_names.contains(atr)) {
|
||||
name = m_atr_names.value(atr);
|
||||
}
|
||||
// Add the firmware version and the serial number
|
||||
uint8_t version[3] = {0};
|
||||
getStatus(satr, version);
|
||||
name += QString(" v%1.%2.%3")
|
||||
.arg(QString::number(version[0]),
|
||||
QString::number(version[1]),
|
||||
QString::number(version[2]));
|
||||
|
||||
unsigned int serial = 0;
|
||||
getSerial(satr, serial);
|
||||
|
||||
/* This variable indicates that the key is locked / timed out.
|
||||
When using the key via NFC, the user has to re-present the key to clear the timeout.
|
||||
Also, the key can be programmatically reset (see below).
|
||||
When using the key via USB (where the Yubikey presents as a PCSC reader in itself),
|
||||
the non-HMAC-SHA1 slots (eg. OTP) are incorrectly recognized as locked HMAC-SHA1 slots.
|
||||
Due to this conundrum, we exclude "locked" keys from the key enumeration,
|
||||
but only if the reader is the "virtual yubikey reader device".
|
||||
This also has the nice side effect of de-duplicating interfaces when a key
|
||||
Is connected via USB and also accessible via PCSC */
|
||||
bool wouldBlock = false;
|
||||
/* When the key is used via NFC, the lock state / time-out is cleared when
|
||||
the smartcard connection is re-established / the applet is selected
|
||||
so the next call to performTestChallenge actually clears the lock.
|
||||
Due to this the key is unlocked, and we display it as such.
|
||||
When the key times out in the time between the key listing and
|
||||
the database unlock /save, an interaction request will be displayed. */
|
||||
for (int slot = 1; slot <= 2; ++slot) {
|
||||
if (performTestChallenge(&satr, slot, &wouldBlock)) {
|
||||
auto display = tr("(PCSC) %1 [%2] Challenge-Response - Slot %3")
|
||||
.arg(name, QString::number(serial), QString::number(slot));
|
||||
m_foundKeys.insert(serial, {slot, display});
|
||||
}
|
||||
/* This variable indicates that the key is locked / timed out.
|
||||
When using the key via NFC, the user has to re-present the key to clear the timeout.
|
||||
Also, the key can be programmatically reset (see below).
|
||||
When using the key via USB (where the Yubikey presents as a PCSC reader in itself),
|
||||
the non-HMAC-SHA1 slots (eg. OTP) are incorrectly recognized as locked HMAC-SHA1 slots.
|
||||
Due to this conundrum, we exclude "locked" keys from the key enumeration,
|
||||
but only if the reader is the "virtual yubikey reader device".
|
||||
This also has the nice side effect of de-duplicating interfaces when a key
|
||||
Is connected via USB and also accessible via PCSC */
|
||||
bool wouldBlock = false;
|
||||
/* When the key is used via NFC, the lock state / time-out is cleared when
|
||||
the smartcard connection is re-established / the applet is selected
|
||||
so the next call to performTestChallenge actually clears the lock.
|
||||
Due to this the key is unlocked, and we display it as such.
|
||||
When the key times out in the time between the key listing and
|
||||
the database unlock /save, an interaction request will be displayed. */
|
||||
for (int slot = 1; slot <= 2; ++slot) {
|
||||
if (performTestChallenge(&satr, slot, &wouldBlock)) {
|
||||
auto display = tr("(PCSC) %1 [%2] Challenge-Response - Slot %3")
|
||||
.arg(name, QString::number(serial), QString::number(slot));
|
||||
m_foundKeys.insert(serial, {slot, display});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
emit detectComplete(!m_foundKeys.isEmpty());
|
||||
});
|
||||
return !m_foundKeys.isEmpty();
|
||||
}
|
||||
|
||||
bool YubiKeyInterfacePCSC::testChallenge(YubiKeySlot slot, bool* wouldBlock)
|
||||
@ -704,7 +692,7 @@ YubiKeyInterfacePCSC::challenge(YubiKeySlot slot, const QByteArray& challenge, B
|
||||
}
|
||||
|
||||
if (--tries > 0) {
|
||||
QThread::msleep(250);
|
||||
Tools::sleep(250);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ class YubiKeyInterfacePCSC : public YubiKeyInterface
|
||||
public:
|
||||
static YubiKeyInterfacePCSC* instance();
|
||||
|
||||
void findValidKeys() override;
|
||||
bool findValidKeys() override;
|
||||
|
||||
YubiKey::ChallengeResult
|
||||
challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_vector<char>& response) override;
|
||||
|
@ -24,8 +24,6 @@
|
||||
#include "thirdparty/ykcore/ykdef.h"
|
||||
#include "thirdparty/ykcore/ykstatus.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int MAX_KEYS = 4;
|
||||
@ -109,88 +107,80 @@ YubiKeyInterfaceUSB* YubiKeyInterfaceUSB::instance()
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void YubiKeyInterfaceUSB::findValidKeys()
|
||||
bool YubiKeyInterfaceUSB::findValidKeys()
|
||||
{
|
||||
m_error.clear();
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QtConcurrent::run([this] {
|
||||
if (!m_mutex.tryLock(1000)) {
|
||||
emit detectComplete(false);
|
||||
return;
|
||||
}
|
||||
// Remove all known keys
|
||||
m_foundKeys.clear();
|
||||
|
||||
// Remove all known keys
|
||||
m_foundKeys.clear();
|
||||
// Try to detect up to 4 connected hardware keys
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
auto yk_key = openKey(i);
|
||||
if (yk_key) {
|
||||
auto serial = getSerial(yk_key);
|
||||
if (serial == 0) {
|
||||
closeKey(yk_key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to detect up to 4 connected hardware keys
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
auto yk_key = openKey(i);
|
||||
if (yk_key) {
|
||||
auto serial = getSerial(yk_key);
|
||||
if (serial == 0) {
|
||||
closeKey(yk_key);
|
||||
auto st = ykds_alloc();
|
||||
yk_get_status(yk_key, st);
|
||||
int vid, pid;
|
||||
yk_get_key_vid_pid(yk_key, &vid, &pid);
|
||||
|
||||
QString name = m_pid_names.value(pid, tr("Unknown"));
|
||||
if (vid == 0x1d50) {
|
||||
name = QStringLiteral("OnlyKey");
|
||||
}
|
||||
name += QString(" v%1.%2.%3")
|
||||
.arg(QString::number(ykds_version_major(st)),
|
||||
QString::number(ykds_version_minor(st)),
|
||||
QString::number(ykds_version_build(st)));
|
||||
|
||||
bool wouldBlock;
|
||||
for (int slot = 1; slot <= 2; ++slot) {
|
||||
auto config = (slot == 1 ? CONFIG1_VALID : CONFIG2_VALID);
|
||||
if (!(ykds_touch_level(st) & config)) {
|
||||
// Slot is not configured
|
||||
continue;
|
||||
}
|
||||
|
||||
auto st = ykds_alloc();
|
||||
yk_get_status(yk_key, st);
|
||||
int vid, pid;
|
||||
yk_get_key_vid_pid(yk_key, &vid, &pid);
|
||||
|
||||
QString name = m_pid_names.value(pid, tr("Unknown"));
|
||||
if (vid == 0x1d50) {
|
||||
name = QStringLiteral("OnlyKey");
|
||||
// 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")
|
||||
.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")
|
||||
.arg(name,
|
||||
QString::number(serial),
|
||||
QString::number(slot),
|
||||
wouldBlock ? tr("Press", "USB Challenge-Response Key interaction request")
|
||||
: tr("Passive", "USB Challenge-Response Key no interaction required"));
|
||||
m_foundKeys.insert(serial, {slot, display});
|
||||
}
|
||||
name += QString(" v%1.%2.%3")
|
||||
.arg(QString::number(ykds_version_major(st)),
|
||||
QString::number(ykds_version_minor(st)),
|
||||
QString::number(ykds_version_build(st)));
|
||||
|
||||
bool wouldBlock;
|
||||
for (int slot = 1; slot <= 2; ++slot) {
|
||||
auto config = (slot == 1 ? CONFIG1_VALID : CONFIG2_VALID);
|
||||
if (!(ykds_touch_level(st) & config)) {
|
||||
// Slot is not configured
|
||||
continue;
|
||||
}
|
||||
// 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")
|
||||
.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")
|
||||
.arg(name,
|
||||
QString::number(serial),
|
||||
QString::number(slot),
|
||||
wouldBlock ? tr("Press", "USB Challenge-Response Key interaction request")
|
||||
: tr("Passive", "USB Challenge-Response Key no interaction required"));
|
||||
m_foundKeys.insert(serial, {slot, display});
|
||||
}
|
||||
}
|
||||
|
||||
ykds_free(st);
|
||||
closeKey(yk_key);
|
||||
|
||||
Tools::wait(100);
|
||||
} else if (yk_errno == YK_ENOKEY) {
|
||||
// No more keys are connected
|
||||
break;
|
||||
} else if (yk_errno == YK_EUSBERR) {
|
||||
qWarning("Hardware key USB error: %s", yk_usb_strerror());
|
||||
} else {
|
||||
qWarning("Hardware key error: %s", yk_strerror(yk_errno));
|
||||
}
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
emit detectComplete(!m_foundKeys.isEmpty());
|
||||
});
|
||||
ykds_free(st);
|
||||
closeKey(yk_key);
|
||||
|
||||
Tools::wait(100);
|
||||
} else if (yk_errno == YK_ENOKEY) {
|
||||
// No more keys are connected
|
||||
break;
|
||||
} else if (yk_errno == YK_EUSBERR) {
|
||||
qWarning("Hardware key USB error: %s", yk_usb_strerror());
|
||||
} else {
|
||||
qWarning("Hardware key error: %s", yk_strerror(yk_errno));
|
||||
}
|
||||
}
|
||||
|
||||
return !m_foundKeys.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ class YubiKeyInterfaceUSB : public YubiKeyInterface
|
||||
public:
|
||||
static YubiKeyInterfaceUSB* instance();
|
||||
|
||||
void findValidKeys() override;
|
||||
bool findValidKeys() override;
|
||||
|
||||
YubiKey::ChallengeResult
|
||||
challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_vector<char>& response) override;
|
||||
|
@ -38,7 +38,12 @@ bool YubiKey::isInitialized()
|
||||
return false;
|
||||
}
|
||||
|
||||
void YubiKey::findValidKeys()
|
||||
bool YubiKey::findValidKeys()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void YubiKey::findValidKeysAsync()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2075,10 +2075,6 @@ void TestCli::testYubiKeyOption()
|
||||
|
||||
YubiKey::instance()->findValidKeys();
|
||||
|
||||
// Wait for the hardware to respond
|
||||
QSignalSpy detected(YubiKey::instance(), SIGNAL(detectComplete(bool)));
|
||||
QTRY_VERIFY_WITH_TIMEOUT(detected.count() > 0, 2000);
|
||||
|
||||
auto keys = YubiKey::instance()->foundKeys();
|
||||
if (keys.isEmpty()) {
|
||||
QSKIP("No YubiKey devices were detected.");
|
||||
@ -2094,7 +2090,7 @@ void TestCli::testYubiKeyOption()
|
||||
for (auto key : keys) {
|
||||
if (YubiKey::instance()->testChallenge(key, &wouldBlock) && !wouldBlock) {
|
||||
YubiKey::instance()->challenge(key, challenge, response);
|
||||
if (std::memcmp(response.data(), expected.data(), expected.size())) {
|
||||
if (std::memcmp(response.data(), expected.data(), expected.size()) == 0) {
|
||||
pKey = key;
|
||||
break;
|
||||
}
|
||||
@ -2110,7 +2106,11 @@ void TestCli::testYubiKeyOption()
|
||||
Add addCmd;
|
||||
|
||||
setInput("a");
|
||||
execCmd(listCmd, {"ls", "-y", "2", m_yubiKeyProtectedDbFile->fileName()});
|
||||
execCmd(listCmd,
|
||||
{"ls",
|
||||
"-y",
|
||||
QString("%1:%2").arg(QString::number(pKey.second), QString::number(pKey.first)),
|
||||
m_yubiKeyProtectedDbFile->fileName()});
|
||||
m_stderr->readLine(); // skip password prompt
|
||||
QCOMPARE(m_stderr->readAll(), QByteArray());
|
||||
QCOMPARE(m_stdout->readAll(),
|
||||
|
@ -43,10 +43,6 @@ void TestYubiKeyChallengeResponse::testDetectDevices()
|
||||
{
|
||||
YubiKey::instance()->findValidKeys();
|
||||
|
||||
// Wait for the hardware to respond
|
||||
QSignalSpy detected(YubiKey::instance(), SIGNAL(detectComplete(bool)));
|
||||
QTRY_VERIFY_WITH_TIMEOUT(detected.count() > 0, 2000);
|
||||
|
||||
// Look at the information retrieved from the key(s)
|
||||
for (auto key : YubiKey::instance()->foundKeys()) {
|
||||
auto displayName = YubiKey::instance()->getDisplayName(key);
|
||||
|
Loading…
Reference in New Issue
Block a user