From 2faa8f6782a2e6294ed8849048a281d12d60da1c Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Mon, 4 Dec 2017 16:29:56 +0100 Subject: Initial support for multiple devices with C++ and test Signed-off-by: Szczepan Zalega --- .idea/vcs.xml | 3 -- CMakeLists.txt | 3 ++ NitrokeyManager.cc | 30 +++++++++++++ device.cc | 29 +++++++++++- include/NitrokeyManager.h | 7 ++- include/device.h | 10 ++++- unittest/test_multiple_devices.cc | 92 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 unittest/test_multiple_devices.cc diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 486a99a..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,8 +2,5 @@ - - - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a4ebb0..3d788cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,9 @@ IF (COMPILE_TESTS) add_executable (test_issues unittest/test_issues.cc) target_link_libraries (test_issues ${EXTRA_LIBS} nitrokey catch) + add_executable (test_multiple_devices unittest/test_multiple_devices.cc) + target_link_libraries (test_multiple_devices ${EXTRA_LIBS} ${LIBNAME} catch) + ENDIF() diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index d563b26..da5f61a 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -81,6 +81,11 @@ using nitrokey::misc::strcpyT; set_debug(true); } NitrokeyManager::~NitrokeyManager() { + for (auto d : connected_devices){ + if (d.second == nullptr) continue; + d.second->disconnect(); + connected_devices[d.first] = nullptr; + } } bool NitrokeyManager::set_current_device_speed(int retry_delay, int send_receive_delay){ @@ -98,6 +103,31 @@ using nitrokey::misc::strcpyT; return true; } + std::vector NitrokeyManager::list_devices(){ + auto p = make_shared(); + return p->enumerate(); // make static + } + + bool NitrokeyManager::connect_with_path(std::string path) { + std::lock_guard lock(mex_dev_com_manager); + + if(connected_devices.find(path) != connected_devices.end() + && connected_devices[path] != nullptr) { + device = connected_devices[path]; + return true; + } + + + auto p = make_shared(); + p->set_path(path); + + if(!p->connect()) return false; + + connected_devices [path] = p; + device = p; + return true; + } + bool NitrokeyManager::connect() { std::lock_guard lock(mex_dev_com_manager); vector< shared_ptr > devices = { make_shared(), make_shared() }; diff --git a/device.cc b/device.cc index ac31b1d..5a83e2e 100644 --- a/device.cc +++ b/device.cc @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -92,12 +93,20 @@ bool Device::_connect() { LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2); // hid_init(); // done automatically on hid_open - mp_devhandle = hid_open(m_vid, m_pid, nullptr); + if (m_path.empty()){ + mp_devhandle = hid_open(m_vid, m_pid, nullptr); + } else { + mp_devhandle = hid_open_path(m_path.c_str()); + } const bool success = mp_devhandle != nullptr; LOG(std::string("Connection success: ") + std::to_string(success), Loglevel::DEBUG_L2); return success; } +void Device::set_path(const std::string path){ + m_path = path; +} + int Device::send(const void *packet) { LOG(__FUNCTION__, Loglevel::DEBUG_L2); std::lock_guard lock(mex_dev_com); @@ -160,6 +169,24 @@ int Device::recv(void *packet) { return status; } +std::vector Device::enumerate(){ + //TODO make static + auto pInfo = hid_enumerate(m_vid, m_pid); + auto pInfo_ = pInfo; + std::vector res; + while (pInfo != nullptr){ + std::string a (pInfo->path); + res.push_back(a); + pInfo = pInfo->next; + } + + if (pInfo_ != nullptr){ + hid_free_enumeration(pInfo_); + } + + return res; +} + bool Device::could_be_enumerated() { LOG(__FUNCTION__, Loglevel::DEBUG_L2); std::lock_guard lock(mex_dev_com); diff --git a/include/NitrokeyManager.h b/include/NitrokeyManager.h index 0db0856..2a79922 100644 --- a/include/NitrokeyManager.h +++ b/include/NitrokeyManager.h @@ -30,6 +30,7 @@ #include "stick20_commands.h" #include #include +#include namespace nitrokey { using namespace nitrokey::device; @@ -67,6 +68,8 @@ char * strndup(const char* str, size_t maxlen); bool get_time(uint64_t time = 0); bool erase_totp_slot(uint8_t slot_number, const char *temporary_password); bool erase_hotp_slot(uint8_t slot_number, const char *temporary_password); + std::vector list_devices(); + bool connect_with_path (std::string path); bool connect(const char *device_model); bool connect(); bool disconnect(); @@ -198,8 +201,10 @@ char * strndup(const char* str, size_t maxlen); static shared_ptr _instance; std::shared_ptr device; + std::unordered_map > connected_devices; - stick10::ReadSlot::ResponsePayload get_OTP_slot_data(const uint8_t slot_number); + + stick10::ReadSlot::ResponsePayload get_OTP_slot_data(const uint8_t slot_number); bool is_valid_hotp_slot_number(uint8_t slot_number) const; bool is_valid_totp_slot_number(uint8_t slot_number) const; bool is_valid_password_safe_slot_number(uint8_t slot_number) const; diff --git a/include/device.h b/include/device.h index 1bd4773..f6d2380 100644 --- a/include/device.h +++ b/include/device.h @@ -25,6 +25,7 @@ #include "hidapi/hidapi.h" #include #include +#include #define HID_REPORT_SIZE 65 @@ -105,8 +106,10 @@ public: * @return true if visible by OS */ bool could_be_enumerated(); + std::vector enumerate(); - void show_stats(); + + void show_stats(); // ErrorCounters get_stats(){ return m_counters; } int get_retry_receiving_count() const { return m_retry_receiving_count; }; int get_retry_sending_count() const { return m_retry_sending_count; }; @@ -121,8 +124,10 @@ public: void set_retry_delay(std::chrono::milliseconds delay); static void set_default_device_speed(int delay); void setDefaultDelay(); + void set_path(const std::string path); + -private: + private: std::atomic last_command_status; void _reconnect(); bool _connect(); @@ -143,6 +148,7 @@ protected: std::chrono::milliseconds m_retry_timeout; std::chrono::milliseconds m_send_receive_delay; std::atomicmp_devhandle; + std::string m_path; static std::atomic_int instances_count; static std::chrono::milliseconds default_delay ; diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc new file mode 100644 index 0000000..f5b4d6e --- /dev/null +++ b/unittest/test_multiple_devices.cc @@ -0,0 +1,92 @@ + +static const char *const default_admin_pin = "12345678"; +static const char *const default_user_pin = "123456"; +const char * temporary_password = "123456789012345678901234"; +const char * RFC_SECRET = "12345678901234567890"; + +#include "catch.hpp" + +#include +#include +#include +#include + +using namespace nitrokey; + + +TEST_CASE("List devices", "[BASIC]") { + shared_ptr d = make_shared(); + auto v = d->enumerate(); + REQUIRE(v.size() > 0); + for (auto a : v){ + std::cout << a; + d->set_path(a); + d->connect(); + auto res = GetStatus::CommandTransaction::run(d); + auto res2 = GetDeviceStatus::CommandTransaction::run(d); + std::cout << " " << res.data().card_serial_u32 << " " + << res.data().get_card_serial_hex() + << " " << std::to_string(res2.data().versionInfo.minor) + << std::endl; + d->disconnect(); + } +} + +TEST_CASE("Regenerate AES keys", "[BASIC]") { + shared_ptr d = make_shared(); + auto v = d->enumerate(); + REQUIRE(v.size() > 0); + + std::vector> devices; + for (auto a : v){ + std::cout << a << endl; + d = make_shared(); + d->set_path(a); + d->connect(); + devices.push_back(d); + } + + for (auto d : devices){ + auto res2 = GetDeviceStatus::CommandTransaction::run(d); + std::cout << std::to_string(res2.data().versionInfo.minor) << std::endl; +// nitrokey::proto::stick20::CreateNewKeys::CommandPayload p; +// p.set_defaults(); +// memcpy(p.password, "12345678", 8); +// auto res3 = nitrokey::proto::stick20::CreateNewKeys::CommandTransaction::run(d, p); + } + + for (auto d : devices){ + //TODO watch out for multiple hid_exit calls + d->disconnect(); + } +} + + +TEST_CASE("Use API", "[BASIC]") { + auto nm = NitrokeyManager::instance(); + nm->set_loglevel(2); + auto v = nm->list_devices(); + REQUIRE(v.size() > 0); + + for (int i=0; i<10; i++){ + for (auto a : v) { + std::cout <<"Connect with: " << a << + " " << std::boolalpha << nm->connect_with_path(a) << " "; + try{ + auto status_storage = nm->get_status_storage(); + std::cout << status_storage.ActiveSmartCardID_u32 + << " " << status_storage.ActiveSD_CardID_u32 + << std::endl; + + nm->fill_SD_card_with_random_data("12345678"); + } + catch (const LongOperationInProgressException &e){ + std::cout << "long operation in progress on " << a + << " " << std::to_string(e.progress_bar_value) << std::endl; + this_thread::sleep_for(1000ms); + } + } + std::cout <<"Iteration: " << i << std::endl; + } + +} \ No newline at end of file -- cgit v1.2.3