mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
keys: yk: Add YubiKey hardware driver support
* Use compile time detection of the YubiKey libraries and link against the libraries if present. Can be disabled with: $ cmake -DCMAKE_DISABLE_FIND_PACKAGE_YubiKey=FALSE * A stub file provides empty calls for all the function calls integrated in to the UI to support this. In the future a more modular approach maybe better, but opting for simplicity initially. Signed-off-by: Kyle Manna <kyle@kylemanna.com>
This commit is contained in:
parent
add4846d79
commit
82aed2caab
@ -191,6 +191,14 @@ if(NOT ZLIB_SUPPORTS_GZIP)
|
|||||||
message(FATAL_ERROR "zlib 1.2.x or higher is required to use the gzip format")
|
message(FATAL_ERROR "zlib 1.2.x or higher is required to use the gzip format")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
find_package(YubiKey)
|
||||||
|
|
||||||
|
if(YUBIKEY_FOUND)
|
||||||
|
include_directories(SYSTEM ${YUBIKEY_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
check_cxx_source_compiles("#include <sys/prctl.h>
|
check_cxx_source_compiles("#include <sys/prctl.h>
|
||||||
int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }"
|
int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }"
|
||||||
|
29
cmake/FindYubiKey.cmake
Normal file
29
cmake/FindYubiKey.cmake
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# 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/>.
|
||||||
|
|
||||||
|
find_path(YUBIKEY_CORE_INCLUDE_DIR yubikey.h)
|
||||||
|
find_path(YUBIKEY_PERS_INCLUDE_DIR ykcore.h PATH_SUFFIXES ykpers-1)
|
||||||
|
set(YUBIKEY_INCLUDE_DIRS ${YUBIKEY_CORE_INCLUDE_DIR} ${YUBIKEY_PERS_INCLUDE_DIR})
|
||||||
|
|
||||||
|
find_library(YUBIKEY_CORE_LIBRARY yubikey)
|
||||||
|
find_library(YUBIKEY_PERS_LIBRARY ykpers-1)
|
||||||
|
set(YUBIKEY_LIBRARIES ${YUBIKEY_CORE_LIBRARY} ${YUBIKEY_PERS_LIBRARY})
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(YubiKey DEFAULT_MSG YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
# TODO: Is mark_as_advanced() necessary? It's used in many examples with
|
||||||
|
# little explanation. Disable for now in favor of simplicity.
|
||||||
|
#mark_as_advanced(YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS)
|
@ -111,6 +111,7 @@ set(keepassx_SOURCES
|
|||||||
gui/group/GroupView.cpp
|
gui/group/GroupView.cpp
|
||||||
keys/CompositeKey.cpp
|
keys/CompositeKey.cpp
|
||||||
keys/CompositeKey_p.h
|
keys/CompositeKey_p.h
|
||||||
|
keys/drivers/YubiKey.h
|
||||||
keys/FileKey.cpp
|
keys/FileKey.cpp
|
||||||
keys/Key.h
|
keys/Key.h
|
||||||
keys/PasswordKey.cpp
|
keys/PasswordKey.cpp
|
||||||
@ -190,6 +191,12 @@ if(MINGW)
|
|||||||
${CMAKE_SOURCE_DIR}/share/windows/icon.rc)
|
${CMAKE_SOURCE_DIR}/share/windows/icon.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(YUBIKEY_FOUND)
|
||||||
|
set(keepassx_SOURCES ${keepassx_SOURCES} keys/drivers/YubiKey.cpp)
|
||||||
|
else()
|
||||||
|
set(keepassx_SOURCES ${keepassx_SOURCES} keys/drivers/YubiKeyStub.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
qt5_wrap_ui(keepassx_SOURCES ${keepassx_FORMS})
|
qt5_wrap_ui(keepassx_SOURCES ${keepassx_FORMS})
|
||||||
|
|
||||||
add_library(zxcvbn STATIC zxcvbn/zxcvbn.cpp)
|
add_library(zxcvbn STATIC zxcvbn/zxcvbn.cpp)
|
||||||
@ -220,6 +227,10 @@ target_link_libraries(${PROGNAME}
|
|||||||
${GCRYPT_LIBRARIES}
|
${GCRYPT_LIBRARIES}
|
||||||
${ZLIB_LIBRARIES})
|
${ZLIB_LIBRARIES})
|
||||||
|
|
||||||
|
if(YUBIKEY_FOUND)
|
||||||
|
target_link_libraries(keepassx_core ${YUBIKEY_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON)
|
set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
245
src/keys/drivers/YubiKey.cpp
Normal file
245
src/keys/drivers/YubiKey.cpp
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
70
src/keys/drivers/YubiKey.h
Normal file
70
src/keys/drivers/YubiKey.h
Normal file
@ -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
|
71
src/keys/drivers/YubiKeyStub.cpp
Normal file
71
src/keys/drivers/YubiKeyStub.cpp
Normal file
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user