Use QSharedPointer instead of cloning YkChallengeResponseKey and make it a QObject to allow emitting signals

This commit is contained in:
Janek Bevendorff 2017-02-23 23:52:36 +01:00
parent e93e4a9931
commit 093fe5c7ef
No known key found for this signature in database
GPG Key ID: CFEC2F6850BFFA53
9 changed files with 78 additions and 66 deletions

View File

@ -257,18 +257,16 @@ bool Database::verifyKey(const CompositeKey& key) const
{ {
Q_ASSERT(hasKey()); Q_ASSERT(hasKey());
/* If the database has challenge response keys, then the the verification
* key better as well */
if (!m_data.challengeResponseKey.isEmpty()) { if (!m_data.challengeResponseKey.isEmpty()) {
QByteArray result; QByteArray result;
if (!key.challenge(m_data.masterSeed, result)) { if (!key.challenge(m_data.masterSeed, result)) {
/* Challenge failed, (YubiKey?) removed? */ // challenge failed, (YubiKey?) removed?
return false; return false;
} }
if (m_data.challengeResponseKey != result) { if (m_data.challengeResponseKey != result) {
/* Wrong response from challenged device(s) */ // wrong response from challenged device(s)
return false; return false;
} }
} }

View File

@ -115,7 +115,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
if (m_db->challengeMasterSeed(m_masterSeed) == false) { if (m_db->challengeMasterSeed(m_masterSeed) == false) {
raiseError(tr("Unable to issue challenge-response.")); raiseError(tr("Unable to issue challenge-response."));
return Q_NULLPTR; return nullptr;
} }
CryptoHash hash(CryptoHash::Sha256); CryptoHash hash(CryptoHash::Sha256);

View File

@ -15,8 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QtConcurrentRun>
#include "ChangeMasterKeyWidget.h" #include "ChangeMasterKeyWidget.h"
#include "ui_ChangeMasterKeyWidget.h" #include "ui_ChangeMasterKeyWidget.h"
@ -30,6 +28,9 @@
#include "config-keepassx.h" #include "config-keepassx.h"
#include <QtConcurrentRun>
#include <QSharedPointer>
ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent)
: DialogyWidget(parent) : DialogyWidget(parent)
, m_ui(new Ui::ChangeMasterKeyWidget()) , m_ui(new Ui::ChangeMasterKeyWidget())
@ -172,9 +173,9 @@ void ChangeMasterKeyWidget::generateKey()
MessageWidget::Error); MessageWidget::Error);
return; return;
} }
bool blocking = i & true;
YkChallengeResponseKey key(i); int slot = i >> 1;
auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
m_key.addChallengeResponseKey(key); m_key.addChallengeResponseKey(key);
} }
#endif #endif
@ -210,7 +211,7 @@ void ChangeMasterKeyWidget::pollYubikey()
void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking) void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking)
{ {
YkChallengeResponseKey yk(slot, blocking); YkChallengeResponseKey yk(slot, blocking);
m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking));
m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked());
m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked());
m_ui->yubikeyProgress->setVisible(false); m_ui->yubikeyProgress->setVisible(false);

View File

@ -15,8 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QtConcurrentRun>
#include "DatabaseOpenWidget.h" #include "DatabaseOpenWidget.h"
#include "ui_DatabaseOpenWidget.h" #include "ui_DatabaseOpenWidget.h"
@ -34,6 +32,9 @@
#include "config-keepassx.h" #include "config-keepassx.h"
#include <QtConcurrentRun>
#include <QSharedPointer>
DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
: DialogyWidget(parent), : DialogyWidget(parent),
@ -156,7 +157,7 @@ void DatabaseOpenWidget::openDatabase()
if (m_ui->messageWidget->isVisible()) { if (m_ui->messageWidget->isVisible()) {
m_ui->messageWidget->animatedHide(); m_ui->messageWidget->animatedHide();
} }
Q_EMIT editFinished(true); emit editFinished(true);
} }
else { else {
m_ui->messageWidget->showMessage(tr("Unable to open the database.") m_ui->messageWidget->showMessage(tr("Unable to open the database.")
@ -209,8 +210,10 @@ CompositeKey DatabaseOpenWidget::databaseKey()
if (m_ui->checkChallengeResponse->isChecked()) { if (m_ui->checkChallengeResponse->isChecked()) {
int i = m_ui->comboChallengeResponse->currentIndex(); int i = m_ui->comboChallengeResponse->currentIndex();
i = m_ui->comboChallengeResponse->itemData(i).toInt(); i = m_ui->comboChallengeResponse->itemData(i).toInt();
YkChallengeResponseKey key(i);
bool blocking = i & true;
int slot = i >> 1;
auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
masterKey.addChallengeResponseKey(key); masterKey.addChallengeResponseKey(key);
} }
#endif #endif
@ -250,20 +253,21 @@ void DatabaseOpenWidget::browseKeyFile()
void DatabaseOpenWidget::pollYubikey() void DatabaseOpenWidget::pollYubikey()
{ {
// YubiKey init is slow, detect asynchronously to not block the UI
m_ui->buttonRedetectYubikey->setEnabled(false); m_ui->buttonRedetectYubikey->setEnabled(false);
m_ui->checkChallengeResponse->setEnabled(false); m_ui->checkChallengeResponse->setEnabled(false);
m_ui->checkChallengeResponse->setChecked(false); m_ui->checkChallengeResponse->setChecked(false);
m_ui->comboChallengeResponse->setEnabled(false); m_ui->comboChallengeResponse->setEnabled(false);
m_ui->comboChallengeResponse->clear(); m_ui->comboChallengeResponse->clear();
m_ui->yubikeyProgress->setVisible(true); m_ui->yubikeyProgress->setVisible(true);
// YubiKey init is slow, detect asynchronously to not block the UI
QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); QtConcurrent::run(YubiKey::instance(), &YubiKey::detect);
} }
void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
{ {
YkChallengeResponseKey yk(slot, blocking); YkChallengeResponseKey yk(slot, blocking);
m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking));
m_ui->comboChallengeResponse->setEnabled(true); m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true); m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true); m_ui->buttonRedetectYubikey->setEnabled(true);

View File

@ -25,7 +25,6 @@ class ChallengeResponseKey
public: public:
virtual ~ChallengeResponseKey() {} virtual ~ChallengeResponseKey() {}
virtual QByteArray rawKey() const = 0; virtual QByteArray rawKey() const = 0;
virtual ChallengeResponseKey* clone() const = 0;
virtual bool challenge(const QByteArray& challenge) = 0; virtual bool challenge(const QByteArray& challenge) = 0;
}; };

View File

@ -46,7 +46,6 @@ CompositeKey::~CompositeKey()
void CompositeKey::clear() void CompositeKey::clear()
{ {
qDeleteAll(m_keys); qDeleteAll(m_keys);
qDeleteAll(m_challengeResponseKeys);
m_keys.clear(); m_keys.clear();
m_challengeResponseKeys.clear(); m_challengeResponseKeys.clear();
} }
@ -73,8 +72,8 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key)
for (const Key* subKey : asConst(key.m_keys)) { for (const Key* subKey : asConst(key.m_keys)) {
addKey(*subKey); addKey(*subKey);
} }
for (const ChallengeResponseKey* subKey : asConst(key.m_challengeResponseKeys)) { for (const auto subKey : asConst(key.m_challengeResponseKeys)) {
addChallengeResponseKey(*subKey); addChallengeResponseKey(subKey);
} }
return *this; return *this;
@ -176,9 +175,8 @@ QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray
bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const
{ {
/* If no challenge response was requested, return nothing to // if no challenge response was requested, return nothing to
* maintain backwards compatability with regular databases. // maintain backwards compatibility with regular databases.
*/
if (m_challengeResponseKeys.length() == 0) { if (m_challengeResponseKeys.length() == 0) {
result.clear(); result.clear();
return true; return true;
@ -186,9 +184,9 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const
CryptoHash cryptoHash(CryptoHash::Sha256); CryptoHash cryptoHash(CryptoHash::Sha256);
for (ChallengeResponseKey* key : m_challengeResponseKeys) { for (const auto key : m_challengeResponseKeys) {
/* If the device isn't present or fails, return an error */ // if the device isn't present or fails, return an error
if (key->challenge(seed) == false) { if (!key->challenge(seed)) {
return false; return false;
} }
cryptoHash.addData(key->rawKey()); cryptoHash.addData(key->rawKey());
@ -203,11 +201,12 @@ void CompositeKey::addKey(const Key& key)
m_keys.append(key.clone()); m_keys.append(key.clone());
} }
void CompositeKey::addChallengeResponseKey(const ChallengeResponseKey& key) void CompositeKey::addChallengeResponseKey(QSharedPointer<ChallengeResponseKey> key)
{ {
m_challengeResponseKeys.append(key.clone()); m_challengeResponseKeys.append(key);
} }
int CompositeKey::transformKeyBenchmark(int msec) int CompositeKey::transformKeyBenchmark(int msec)
{ {
TransformKeyBenchmarkThread thread1(msec); TransformKeyBenchmarkThread thread1(msec);

View File

@ -20,6 +20,7 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QSharedPointer>
#include "keys/Key.h" #include "keys/Key.h"
#include "keys/ChallengeResponseKey.h" #include "keys/ChallengeResponseKey.h"
@ -41,7 +42,7 @@ public:
bool challenge(const QByteArray& seed, QByteArray &result) const; bool challenge(const QByteArray& seed, QByteArray &result) const;
void addKey(const Key& key); void addKey(const Key& key);
void addChallengeResponseKey(const ChallengeResponseKey& key); void addChallengeResponseKey(QSharedPointer<ChallengeResponseKey> key);
static int transformKeyBenchmark(int msec); static int transformKeyBenchmark(int msec);
static CompositeKey readFromLine(QString line); static CompositeKey readFromLine(QString line);
@ -51,7 +52,7 @@ private:
quint64 rounds, bool* ok, QString* errorString); quint64 rounds, bool* ok, QString* errorString);
QList<Key*> m_keys; QList<Key*> m_keys;
QList<ChallengeResponseKey*> m_challengeResponseKeys; QList<QSharedPointer<ChallengeResponseKey>> m_challengeResponseKeys;
}; };
#endif // KEEPASSX_COMPOSITEKEY_H #endif // KEEPASSX_COMPOSITEKEY_H

View File

@ -15,7 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QFile> #include <QFile>
#include <QXmlStreamReader> #include <QXmlStreamReader>
@ -26,11 +25,7 @@
#include "keys/YkChallengeResponseKey.h" #include "keys/YkChallengeResponseKey.h"
#include "keys/drivers/YubiKey.h" #include "keys/drivers/YubiKey.h"
#include <QDebug> YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking)
#include <QObject>
YkChallengeResponseKey::YkChallengeResponseKey(int slot,
bool blocking)
: m_slot(slot), : m_slot(slot),
m_blocking(blocking) m_blocking(blocking)
{ {
@ -41,40 +36,41 @@ QByteArray YkChallengeResponseKey::rawKey() const
return m_key; return m_key;
} }
YkChallengeResponseKey* YkChallengeResponseKey::clone() const /**
{ * Assumes yubikey()->init() was called
return new YkChallengeResponseKey(*this); */
}
/** Assumes yubikey()->init() was called */
bool YkChallengeResponseKey::challenge(const QByteArray& chal) bool YkChallengeResponseKey::challenge(const QByteArray& chal)
{ {
return challenge(chal, 1); return challenge(chal, 1);
} }
#include <QDebug>
bool YkChallengeResponseKey::challenge(const QByteArray& chal, int retries) bool YkChallengeResponseKey::challenge(const QByteArray& chal, unsigned retries)
{ {
if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { Q_ASSERT(retries > 0);
return true;
}
/* If challenge failed, retry to detect YubiKeys in the event the YubiKey do {
* was un-plugged and re-plugged */ --retries;
while (retries > 0) {
#ifdef QT_DEBUG
qDebug() << "Attempt" << retries << "to re-detect YubiKey(s)";
#endif
retries--;
if (YubiKey::instance()->init() != true) { if (m_blocking) {
emit userInteractionRequired();
}
auto result = YubiKey::instance()->challenge(m_slot, true, chal, m_key);
if (m_blocking) {
emit userConfirmed();
}
if (result != YubiKey::ERROR) {
return true;
}
// if challenge failed, retry to detect YubiKeys in the event the YubiKey was un-plugged and re-plugged
if (retries > 0 && YubiKey::instance()->init() != true) {
continue; continue;
} }
if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { } while (retries > 0);
return true;
}
}
return false; return false;
} }

View File

@ -22,20 +22,34 @@
#include "keys/ChallengeResponseKey.h" #include "keys/ChallengeResponseKey.h"
#include "keys/drivers/YubiKey.h" #include "keys/drivers/YubiKey.h"
class YkChallengeResponseKey : public ChallengeResponseKey #include <QObject>
class YkChallengeResponseKey : public QObject, public ChallengeResponseKey
{ {
Q_OBJECT
public: public:
YkChallengeResponseKey(int slot = -1, YkChallengeResponseKey(int slot = -1, bool blocking = false);
bool blocking = false);
QByteArray rawKey() const; QByteArray rawKey() const;
YkChallengeResponseKey* clone() const;
bool challenge(const QByteArray& chal); bool challenge(const QByteArray& chal);
bool challenge(const QByteArray& chal, int retries); bool challenge(const QByteArray& chal, unsigned retries);
QString getName() const; QString getName() const;
bool isBlocking() const; bool isBlocking() const;
signals:
/**
* Emitted whenever user interaction is required to proceed with the challenge-response protocol.
* You can use this to show a helpful dialog informing the user that his assistance is required.
*/
void userInteractionRequired();
/**
* Emitted when the user has provided their required input.
*/
void userConfirmed();
private: private:
QByteArray m_key; QByteArray m_key;
int m_slot; int m_slot;