Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/keys
diff options
context:
space:
mode:
Diffstat (limited to 'src/keys')
-rw-r--r--src/keys/ChallengeResponseKey.h32
-rw-r--r--src/keys/CompositeKey.cpp37
-rw-r--r--src/keys/CompositeKey.h5
-rw-r--r--src/keys/YkChallengeResponseKey.cpp94
-rw-r--r--src/keys/YkChallengeResponseKey.h45
-rw-r--r--src/keys/drivers/YubiKey.cpp250
-rw-r--r--src/keys/drivers/YubiKey.h70
-rw-r--r--src/keys/drivers/YubiKeyStub.cpp71
8 files changed, 603 insertions, 1 deletions
diff --git a/src/keys/ChallengeResponseKey.h b/src/keys/ChallengeResponseKey.h
new file mode 100644
index 000000000..e03a2f9f9
--- /dev/null
+++ b/src/keys/ChallengeResponseKey.h
@@ -0,0 +1,32 @@
+/*
+* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
+*
+* 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_CHALLENGE_RESPONSE_KEY_H
+#define KEEPASSX_CHALLENGE_RESPONSE_KEY_H
+
+#include <QByteArray>
+
+class ChallengeResponseKey
+{
+public:
+ virtual ~ChallengeResponseKey() {}
+ virtual QByteArray rawKey() const = 0;
+ virtual ChallengeResponseKey* clone() const = 0;
+ virtual bool challenge(const QByteArray& challenge) = 0;
+};
+
+#endif // KEEPASSX_CHALLENGE_RESPONSE_KEY_H
diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp
index 88116c104..4e79cd05c 100644
--- a/src/keys/CompositeKey.cpp
+++ b/src/keys/CompositeKey.cpp
@@ -17,6 +17,7 @@
#include "CompositeKey.h"
#include "CompositeKey_p.h"
+#include "ChallengeResponseKey.h"
#include <QElapsedTimer>
#include <QFile>
@@ -45,12 +46,14 @@ CompositeKey::~CompositeKey()
void CompositeKey::clear()
{
qDeleteAll(m_keys);
+ qDeleteAll(m_challengeResponseKeys);
m_keys.clear();
+ m_challengeResponseKeys.clear();
}
bool CompositeKey::isEmpty() const
{
- return m_keys.isEmpty();
+ return m_keys.isEmpty() && m_challengeResponseKeys.isEmpty();
}
CompositeKey* CompositeKey::clone() const
@@ -70,6 +73,9 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key)
for (const Key* subKey : asConst(key.m_keys)) {
addKey(*subKey);
}
+ for (const ChallengeResponseKey* subKey : asConst(key.m_challengeResponseKeys)) {
+ addChallengeResponseKey(*subKey);
+ }
return *this;
}
@@ -168,11 +174,40 @@ QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray
return result;
}
+bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const
+{
+ /* If no challenge response was requested, return nothing to
+ * maintain backwards compatability with regular databases.
+ */
+ if (m_challengeResponseKeys.length() == 0) {
+ result.clear();
+ return true;
+ }
+
+ CryptoHash cryptoHash(CryptoHash::Sha256);
+
+ for (ChallengeResponseKey* key : m_challengeResponseKeys) {
+ /* If the device isn't present or fails, return an error */
+ if (key->challenge(seed) == false) {
+ return false;
+ }
+ cryptoHash.addData(key->rawKey());
+ }
+
+ result = cryptoHash.result();
+ return true;
+}
+
void CompositeKey::addKey(const Key& key)
{
m_keys.append(key.clone());
}
+void CompositeKey::addChallengeResponseKey(const ChallengeResponseKey& key)
+{
+ m_challengeResponseKeys.append(key.clone());
+}
+
int CompositeKey::transformKeyBenchmark(int msec)
{
TransformKeyBenchmarkThread thread1(msec);
diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h
index f8666aadc..531c2d9b2 100644
--- a/src/keys/CompositeKey.h
+++ b/src/keys/CompositeKey.h
@@ -22,6 +22,7 @@
#include <QString>
#include "keys/Key.h"
+#include "keys/ChallengeResponseKey.h"
class CompositeKey : public Key
{
@@ -37,7 +38,10 @@ public:
QByteArray rawKey() const;
QByteArray transform(const QByteArray& seed, quint64 rounds,
bool* ok, QString* errorString) const;
+ bool challenge(const QByteArray& seed, QByteArray &result) const;
+
void addKey(const Key& key);
+ void addChallengeResponseKey(const ChallengeResponseKey& key);
static int transformKeyBenchmark(int msec);
static CompositeKey readFromLine(QString line);
@@ -47,6 +51,7 @@ private:
quint64 rounds, bool* ok, QString* errorString);
QList<Key*> m_keys;
+ QList<ChallengeResponseKey*> m_challengeResponseKeys;
};
#endif // KEEPASSX_COMPOSITEKEY_H
diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp
new file mode 100644
index 000000000..17982ab62
--- /dev/null
+++ b/src/keys/YkChallengeResponseKey.cpp
@@ -0,0 +1,94 @@
+/*
+* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
+*
+* 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 <QFile>
+#include <QXmlStreamReader>
+
+#include "core/Tools.h"
+#include "crypto/CryptoHash.h"
+#include "crypto/Random.h"
+
+#include "keys/YkChallengeResponseKey.h"
+#include "keys/drivers/YubiKey.h"
+
+#include <QDebug>
+
+YkChallengeResponseKey::YkChallengeResponseKey(int slot,
+ bool blocking)
+ : m_slot(slot),
+ m_blocking(blocking)
+{
+}
+
+QByteArray YkChallengeResponseKey::rawKey() const
+{
+ return m_key;
+}
+
+YkChallengeResponseKey* YkChallengeResponseKey::clone() const
+{
+ return new YkChallengeResponseKey(*this);
+}
+
+
+/** Assumes yubikey()->init() was called */
+bool YkChallengeResponseKey::challenge(const QByteArray& chal)
+{
+ return challenge(chal, 1);
+}
+
+bool YkChallengeResponseKey::challenge(const QByteArray& chal, int retries)
+{
+ if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) {
+ return true;
+ }
+
+ /* If challenge failed, retry to detect YubiKeys int the event the YubiKey
+ * was un-plugged and re-plugged */
+ while (retries > 0) {
+ qDebug() << "Attempt" << retries << "to re-detect YubiKey(s)";
+ retries--;
+
+ if (YubiKey::instance()->init() != true) {
+ continue;
+ }
+
+ if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QString YkChallengeResponseKey::getName() const
+{
+ unsigned int serial;
+ QString fmt("YubiKey[%1] Challenge Response - Slot %2 - %3");
+
+ YubiKey::instance()->getSerial(serial);
+
+ return fmt.arg(QString::number(serial),
+ QString::number(m_slot),
+ (m_blocking) ? "Press" : "Passive");
+}
+
+bool YkChallengeResponseKey::isBlocking() const
+{
+ return m_blocking;
+}
diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h
new file mode 100644
index 000000000..8acb0f9e9
--- /dev/null
+++ b/src/keys/YkChallengeResponseKey.h
@@ -0,0 +1,45 @@
+/*
+* Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
+*
+* 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_CHALLENGERESPONSEKEY_H
+#define KEEPASSX_YK_CHALLENGERESPONSEKEY_H
+
+#include "core/Global.h"
+#include "keys/ChallengeResponseKey.h"
+#include "keys/drivers/YubiKey.h"
+
+class YkChallengeResponseKey : public ChallengeResponseKey
+{
+public:
+
+ YkChallengeResponseKey(int slot = -1,
+ bool blocking = false);
+
+ QByteArray rawKey() const;
+ YkChallengeResponseKey* clone() const;
+ bool challenge(const QByteArray& chal);
+ bool challenge(const QByteArray& chal, int retries);
+ QString getName() const;
+ bool isBlocking() const;
+
+private:
+ QByteArray m_key;
+ int m_slot;
+ bool m_blocking;
+};
+
+#endif // KEEPASSX_YK_CHALLENGERESPONSEKEY_H
diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp
new file mode 100644
index 000000000..5ca175836
--- /dev/null
+++ b/src/keys/drivers/YubiKey.cpp
@@ -0,0 +1,250 @@
+/*
+* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
+*
+* 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 <stdio.h>
+
+#include <QDebug>
+
+#include <ykcore.h>
+#include <yubikey.h>
+#include <ykdef.h>
+#include <ykstatus.h>
+
+#include "core/Global.h"
+#include "crypto/Random.h"
+
+#include "YubiKey.h"
+
+/* Cast the void pointer from the generalized class definition
+ * to the proper pointer type from the now included system headers
+ */
+#define m_yk (static_cast<YK_KEY*>(m_yk_void))
+#define m_ykds (static_cast<YK_STATUS*>(m_ykds_void))
+
+YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL)
+{
+}
+
+YubiKey* YubiKey::m_instance(Q_NULLPTR);
+
+/**
+ * @brief YubiKey::instance - get instance of singleton
+ * @return
+ */
+YubiKey* YubiKey::instance()
+{
+ if (!m_instance) {
+ m_instance = new YubiKey();
+ }
+
+ return m_instance;
+}
+
+/**
+ * @brief YubiKey::init - initialize yubikey library and hardware
+ * @return
+ */
+bool YubiKey::init()
+{
+ /* Previously initalized */
+ if (m_yk != NULL && m_ykds != NULL) {
+
+ if (yk_get_status(m_yk, m_ykds)) {
+ /* Still connected */
+ return true;
+ } else {
+ /* Initialized but not connected anymore, re-init */
+ deinit();
+ }
+ }
+
+ if (!yk_init()) {
+ return false;
+ }
+
+ /* TODO: handle multiple attached hardware devices, currently own one */
+ m_yk_void = static_cast<void*>(yk_open_first_key());
+ if (m_yk == NULL) {
+ return false;
+ }
+
+ m_ykds_void = static_cast<void*>(ykds_alloc());
+ if (m_ykds == NULL) {
+ yk_close_key(m_yk);
+ m_yk_void = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief YubiKey::deinit - cleanup after init
+ * @return true on success
+ */
+bool YubiKey::deinit()
+{
+ if (m_yk) {
+ yk_close_key(m_yk);
+ m_yk_void = NULL;
+ }
+
+ if (m_ykds) {
+ ykds_free(m_ykds);
+ m_ykds_void = NULL;
+ }
+
+ return true;
+}
+
+/**
+ * @brief YubiKey::detect - probe for attached YubiKeys
+ */
+void YubiKey::detect()
+{
+ if (init()) {
+
+ for (int i = 1; i < 3; i++) {
+ YubiKey::ChallengeResult result;
+ QByteArray rand = randomGen()->randomArray(1);
+ QByteArray resp;
+
+ result = challenge(i, false, rand, resp);
+
+ if (result != YubiKey::ERROR) {
+ Q_EMIT detected(i, result == YubiKey::WOULDBLOCK ? true : false);
+ }
+ }
+ }
+}
+
+/**
+ * @brief YubiKey::getSerial - serial number of yubikey
+ * @param serial
+ * @return
+ */
+bool YubiKey::getSerial(unsigned int& serial) const
+{
+ if (!yk_get_serial(m_yk, 1, 0, &serial)) {
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef QT_DEBUG
+/**
+ * @brief printByteArray - debug raw data
+ * @param a array input
+ * @return string representation of array
+ */
+static inline QString printByteArray(const QByteArray& a)
+{
+ QString s;
+ for (int i = 0; i < a.size(); i++)
+ s.append(QString::number(a[i] & 0xff, 16).rightJustified(2, '0'));
+ return s;
+}
+#endif
+
+/**
+ * @brief YubiKey::challenge - issue a challenge
+ *
+ * This operation could block if the YubiKey requires a touch to trigger.
+ *
+ * TODO: Signal to the UI that the system is waiting for challenge response
+ * touch.
+ *
+ * @param slot YubiKey configuration slot
+ * @param mayBlock operation is allowed to block
+ * @param chal challenge input to YubiKey
+ * @param resp response output from YubiKey
+ * @return SUCCESS when successful
+ */
+YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock,
+ const QByteArray& chal,
+ QByteArray& resp) const
+{
+ int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2;
+ QByteArray paddedChal = chal;
+
+ /* Ensure that YubiKey::init() succeeded */
+ if (m_yk == NULL) {
+ return ERROR;
+ }
+
+ /* yk_challenge_response() insists on 64 byte response buffer */
+ resp.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
+ * configurations even work, some docs say avoid it.
+ */
+ const int padLen = 64 - paddedChal.size();
+ if (padLen > 0) {
+ paddedChal.append(QByteArray(padLen, padLen));
+ }
+
+ const unsigned char *c;
+ unsigned char *r;
+ c = reinterpret_cast<const unsigned char*>(paddedChal.constData());
+ r = reinterpret_cast<unsigned char*>(resp.data());
+
+#ifdef QT_DEBUG
+ qDebug().nospace() << __func__ << "(" << slot << ") c = "
+ << printByteArray(paddedChal);
+#endif
+
+ int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock,
+ paddedChal.size(), c,
+ resp.size(), r);
+
+ if (!ret) {
+ if (yk_errno == YK_EWOULDBLOCK) {
+ return WOULDBLOCK;
+ } else if (yk_errno == YK_ETIMEOUT) {
+ return ERROR;
+ } else if (yk_errno) {
+
+ /* Something went wrong, close the key, so that the next call to
+ * can try to re-open.
+ *
+ * Likely caused by the YubiKey being unplugged.
+ */
+
+ if (yk_errno == YK_EUSBERR) {
+ qWarning() << "USB error:" << yk_usb_strerror();
+ } else {
+ qWarning() << "YubiKey core error:" << yk_strerror(yk_errno);
+ }
+
+ return ERROR;
+ }
+ }
+
+ /* Actual HMAC-SHA1 response is only 20 bytes */
+ resp.resize(20);
+
+#ifdef QT_DEBUG
+ qDebug().nospace() << __func__ << "(" << slot << ") r = "
+ << printByteArray(resp) << ", ret = " << ret;
+#endif
+
+ return SUCCESS;
+}
diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h
new file mode 100644
index 000000000..492fba01d
--- /dev/null
+++ b/src/keys/drivers/YubiKey.h
@@ -0,0 +1,70 @@
+/*
+* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
+*
+* 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_YUBIKEY_H
+#define KEEPASSX_YUBIKEY_H
+
+#include <QObject>
+
+/**
+ * Singleton class to manage the interface to the hardware
+ */
+class YubiKey : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ChallengeResult { ERROR = -1, SUCCESS = 0, WOULDBLOCK };
+
+ static YubiKey* instance();
+
+ /** Initialize the underlying yubico libraries */
+ bool init();
+ bool deinit();
+
+ /** Issue a challenge to the hardware */
+ ChallengeResult challenge(int slot, bool mayBlock,
+ const QByteArray& chal,
+ QByteArray& resp) const;
+
+ /** Read the serial number from the hardware */
+ bool getSerial(unsigned int& serial) const;
+
+ /** Start looking for attached hardware devices */
+ void detect();
+
+Q_SIGNALS:
+ /** Emitted in response to detect() when a device is found
+ *
+ * @slot is the slot number detected
+ * @blocking signifies if the YK is setup in passive mode or if requires
+ * the user to touch it for a response
+ */
+ void detected(int slot, bool blocking);
+
+private:
+ explicit YubiKey();
+ static YubiKey* m_instance;
+
+ /* Create void ptr here to avoid ifdef header include mess */
+ void *m_yk_void;
+ void *m_ykds_void;
+
+ Q_DISABLE_COPY(YubiKey)
+};
+
+#endif // KEEPASSX_YUBIKEY_H
diff --git a/src/keys/drivers/YubiKeyStub.cpp b/src/keys/drivers/YubiKeyStub.cpp
new file mode 100644
index 000000000..c00790f38
--- /dev/null
+++ b/src/keys/drivers/YubiKeyStub.cpp
@@ -0,0 +1,71 @@
+/*
+* Copyright (C) 2014 Kyle Manna <kyle@kylemanna.com>
+*
+* 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 <stdio.h>
+
+#include "core/Global.h"
+#include "crypto/Random.h"
+
+#include "YubiKey.h"
+
+YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL)
+{
+}
+
+YubiKey* YubiKey::m_instance(Q_NULLPTR);
+
+YubiKey* YubiKey::instance()
+{
+ if (!m_instance) {
+ m_instance = new YubiKey();
+ }
+
+ return m_instance;
+}
+
+bool YubiKey::init()
+{
+ return false;
+}
+
+bool YubiKey::deinit()
+{
+ return false;
+}
+
+void YubiKey::detect()
+{
+}
+
+bool YubiKey::getSerial(unsigned int& serial) const
+{
+ Q_UNUSED(serial);
+
+ return false;
+}
+
+YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock,
+ const QByteArray& chal,
+ QByteArray& resp) const
+{
+ Q_UNUSED(slot);
+ Q_UNUSED(mayBlock);
+ Q_UNUSED(chal);
+ Q_UNUSED(resp);
+
+ return ERROR;
+}