CLI: Add Yubikey unlock support
This commit is contained in:
@@ -66,9 +66,9 @@ QByteArray YkChallengeResponseKey::rawKey() const
|
||||
/**
|
||||
* Assumes yubikey()->init() was called
|
||||
*/
|
||||
bool YkChallengeResponseKey::challenge(const QByteArray& challenge)
|
||||
bool YkChallengeResponseKey::challenge(const QByteArray& c)
|
||||
{
|
||||
return this->challenge(challenge, 2);
|
||||
return challenge(c, 2);
|
||||
}
|
||||
|
||||
bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned int retries)
|
||||
|
||||
71
src/keys/YkChallengeResponseKeyCLI.cpp
Normal file
71
src/keys/YkChallengeResponseKeyCLI.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "keys/YkChallengeResponseKeyCLI.h"
|
||||
#include "keys/drivers/YubiKey.h"
|
||||
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/CryptoHash.h"
|
||||
#include "crypto/Random.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QUuid YkChallengeResponseKeyCLI::UUID("e2be77c0-c810-417a-8437-32f41d00bd1d");
|
||||
|
||||
YkChallengeResponseKeyCLI::YkChallengeResponseKeyCLI(int slot,
|
||||
bool blocking,
|
||||
QString messageInteraction,
|
||||
FILE* outputDescriptor)
|
||||
: ChallengeResponseKey(UUID)
|
||||
, m_slot(slot)
|
||||
, m_blocking(blocking)
|
||||
, m_messageInteraction(messageInteraction)
|
||||
, m_out(outputDescriptor)
|
||||
{
|
||||
}
|
||||
|
||||
QByteArray YkChallengeResponseKeyCLI::rawKey() const
|
||||
{
|
||||
return m_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes yubikey()->init() was called
|
||||
*/
|
||||
bool YkChallengeResponseKeyCLI::challenge(const QByteArray& c)
|
||||
{
|
||||
return challenge(c, 2);
|
||||
}
|
||||
|
||||
bool YkChallengeResponseKeyCLI::challenge(const QByteArray& challenge, unsigned int retries)
|
||||
{
|
||||
QTextStream out(m_out, QIODevice::WriteOnly);
|
||||
do {
|
||||
--retries;
|
||||
|
||||
if (m_blocking) {
|
||||
out << m_messageInteraction << endl;
|
||||
}
|
||||
YubiKey::ChallengeResult result = YubiKey::instance()->challenge(m_slot, m_blocking, challenge, m_key);
|
||||
if (result == YubiKey::SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
} while (retries > 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
52
src/keys/YkChallengeResponseKeyCLI.h
Normal file
52
src/keys/YkChallengeResponseKeyCLI.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H
|
||||
#define KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H
|
||||
|
||||
#include "core/Global.h"
|
||||
#include "keys/ChallengeResponseKey.h"
|
||||
#include "keys/drivers/YubiKey.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
|
||||
class YkChallengeResponseKeyCLI : public QObject, public ChallengeResponseKey
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QUuid UUID;
|
||||
|
||||
explicit YkChallengeResponseKeyCLI(int slot,
|
||||
bool blocking,
|
||||
QString messageInteraction,
|
||||
FILE* outputDescriptor);
|
||||
|
||||
QByteArray rawKey() const override;
|
||||
bool challenge(const QByteArray& challenge) override;
|
||||
bool challenge(const QByteArray& challenge, unsigned int retries);
|
||||
|
||||
private:
|
||||
QByteArray m_key;
|
||||
int m_slot;
|
||||
bool m_blocking;
|
||||
QString m_messageInteraction;
|
||||
FILE* m_out;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H
|
||||
@@ -132,27 +132,17 @@ void YubiKey::detect()
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (init()) {
|
||||
YubiKey::ChallengeResult result;
|
||||
QByteArray rand = randomGen()->randomArray(1);
|
||||
QByteArray resp;
|
||||
|
||||
// Check slot 1 and 2 for Challenge-Response HMAC capability
|
||||
for (int i = 1; i <= 2; ++i) {
|
||||
result = challenge(i, false, rand, resp);
|
||||
if (result == ALREADY_RUNNING) {
|
||||
// Try this slot again after waiting
|
||||
Tools::sleep(300);
|
||||
result = challenge(i, false, rand, resp);
|
||||
}
|
||||
|
||||
if (result != ALREADY_RUNNING && result != ERROR) {
|
||||
emit detected(i, result == WOULDBLOCK);
|
||||
found = true;
|
||||
}
|
||||
// Wait between slots to let the yubikey settle
|
||||
Tools::sleep(150);
|
||||
// Check slot 1 and 2 for Challenge-Response HMAC capability
|
||||
for (int i = 1; i <= 2; ++i) {
|
||||
QString errorMsg;
|
||||
bool isBlocking = checkSlotIsBlocking(i, errorMsg);
|
||||
if (errorMsg.isEmpty()) {
|
||||
found = true;
|
||||
emit detected(i, isBlocking);
|
||||
}
|
||||
|
||||
// Wait between slots to let the yubikey settle.
|
||||
Tools::sleep(150);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@@ -162,6 +152,38 @@ void YubiKey::detect()
|
||||
}
|
||||
}
|
||||
|
||||
bool YubiKey::checkSlotIsBlocking(int slot, QString& errorMessage)
|
||||
{
|
||||
if (!init()) {
|
||||
errorMessage = QString("Could not initialize YubiKey.");
|
||||
return false;
|
||||
}
|
||||
|
||||
YubiKey::ChallengeResult result;
|
||||
QByteArray rand = randomGen()->randomArray(1);
|
||||
QByteArray resp;
|
||||
|
||||
result = challenge(slot, false, rand, resp);
|
||||
if (result == ALREADY_RUNNING) {
|
||||
// Try this slot again after waiting
|
||||
Tools::sleep(300);
|
||||
result = challenge(slot, false, rand, resp);
|
||||
}
|
||||
|
||||
if (result == SUCCESS || result == WOULDBLOCK) {
|
||||
return result == WOULDBLOCK;
|
||||
} else if (result == ALREADY_RUNNING) {
|
||||
errorMessage = QString("YubiKey busy");
|
||||
return false;
|
||||
} else if (result == ERROR) {
|
||||
errorMessage = QString("YubiKey error");
|
||||
return false;
|
||||
}
|
||||
|
||||
errorMessage = QString("Error while polling YubiKey");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool YubiKey::getSerial(unsigned int& serial)
|
||||
{
|
||||
m_mutex.lock();
|
||||
@@ -190,14 +212,14 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
|
||||
int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2;
|
||||
QByteArray paddedChallenge = challenge;
|
||||
|
||||
// yk_challenge_response() insists on 64 byte response buffer */
|
||||
// yk_challenge_response() insists on 64 bytes response buffer */
|
||||
response.clear();
|
||||
response.resize(64);
|
||||
|
||||
/* The challenge sent to the yubikey should always be 64 bytes for
|
||||
* compatibility with all configurations. Follow PKCS7 padding.
|
||||
*
|
||||
* There is some question whether or not 64 byte fixed length
|
||||
* There is some question whether or not 64 bytes fixed length
|
||||
* configurations even work, some docs say avoid it.
|
||||
*/
|
||||
const int padLen = 64 - paddedChallenge.size();
|
||||
|
||||
@@ -90,6 +90,13 @@ public:
|
||||
*/
|
||||
void detect();
|
||||
|
||||
/**
|
||||
* @param slot the yubikey slot.
|
||||
* @param errorMessage populated if an error occured.
|
||||
*
|
||||
* @return whether the key is blocking or not.
|
||||
*/
|
||||
bool checkSlotIsBlocking(int slot, QString& errorMessage);
|
||||
signals:
|
||||
/** Emitted in response to detect() when a device is found
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user