From eb55579c1c0e03ea98372280a344c79bb52a1f1a Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:03:06 +0100 Subject: Add DeviceInfo struct for enumeration This is a preparation for a future patch that will change the enumerate method to return a vector of DeviceInfo instances instead of a vector of strings. --- libnitrokey/device.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libnitrokey/device.h b/libnitrokey/device.h index f6d2380..1183c9c 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -50,6 +50,27 @@ enum class DeviceModel{ STORAGE }; +/** + * Information about a connected device. + * + * This struct contains the information about a connected device returned by + * hidapi when enumerating the connected devices. + */ +struct DeviceInfo { + /** + * The model of the connected device. + */ + DeviceModel m_deviceModel; + /** + * The USB connection path for the device. + */ + std::string m_path; + /** + * The serial number of the device. + */ + std::wstring m_serialNumber; +}; + #include class Device { -- cgit v1.2.1 From 4a7ce051bd4004fb62f1c7022d92efa2ce42b6ab Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:03:34 +0100 Subject: Change Device::enumerate return type to use DeviceInfo The return type of Device::enumerate is changed from std::vector to std::vector to expose the additional information contained in the DeviceInfo struct. --- NitrokeyManager.cc | 10 ++++++++-- device.cc | 11 +++++++---- libnitrokey/device.h | 8 +++++++- unittest/test_multiple_devices.cc | 6 ++++-- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index a950e4b..8825fce 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -109,7 +109,12 @@ using nitrokey::misc::strcpyT; std::lock_guard lock(mex_dev_com_manager); auto p = make_shared(); - return p->enumerate(); // make static + auto device_infos = p->enumerate(); + std::vector strings; + strings.resize(device_infos.size()); + std::transform(device_infos.begin(), device_infos.end(), strings.begin(), + [](auto device_info) { return device_info.m_path; }); + return strings; } std::vector NitrokeyManager::list_devices_by_cpuID(){ @@ -130,7 +135,8 @@ using nitrokey::misc::strcpyT; auto d = make_shared(); const auto v = d->enumerate(); LOGD1("Discovering IDs"); - for (auto & p: v){ + for (auto & i: v){ + auto p = i.m_path; d = make_shared(); LOGD1( std::string("Found: ") + p ); d->set_path(p); diff --git a/device.cc b/device.cc index 80e4b38..506a68c 100644 --- a/device.cc +++ b/device.cc @@ -171,14 +171,17 @@ int Device::recv(void *packet) { return status; } -std::vector Device::enumerate(){ +std::vector Device::enumerate(){ //TODO make static auto pInfo = hid_enumerate(m_vid, m_pid); auto pInfo_ = pInfo; - std::vector res; + std::vector res; while (pInfo != nullptr){ - std::string a (pInfo->path); - res.push_back(a); + std::string path(pInfo->path); + std::wstring serialNumber(pInfo->serial_number); + auto deviceModel = this->get_device_model(); + DeviceInfo info = { deviceModel, path, serialNumber }; + res.push_back(info); pInfo = pInfo->next; } diff --git a/libnitrokey/device.h b/libnitrokey/device.h index 1183c9c..1a84402 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -127,7 +127,13 @@ public: * @return true if visible by OS */ bool could_be_enumerated(); - std::vector enumerate(); + /** + * Returns a vector with all connected Nitrokey devices of the same device + * type as this device. + * + * @return information about all connected devices + */ + std::vector enumerate(); void show_stats(); diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index cd78681..b224653 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -37,7 +37,8 @@ TEST_CASE("List devices", "[BASIC]") { shared_ptr d = make_shared(); auto v = d->enumerate(); REQUIRE(v.size() > 0); - for (auto a : v){ + for (auto i : v){ + auto a = i.m_path; std::cout << a; d->set_path(a); d->connect(); @@ -57,7 +58,8 @@ TEST_CASE("Regenerate AES keys", "[BASIC]") { REQUIRE(v.size() > 0); std::vector> devices; - for (auto a : v){ + for (auto i : v){ + auto a = i.m_path; std::cout << a << endl; d = make_shared(); d->set_path(a); -- cgit v1.2.1 From 71d4ecc04c23342f207e7f1133ea8824a1dcdd16 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:03:43 +0100 Subject: Change Nitrokey::list_devices return type to use DeviceInfo In the previous commit, we changed the return value of Device::enumerate to std::vector. Now we change Nitrokey::list_devices to also return DeviceInfo instances. --- NitrokeyManager.cc | 9 ++------- libnitrokey/NitrokeyManager.h | 2 +- unittest/test_multiple_devices.cc | 5 ++++- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 8825fce..8ca4698 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -105,16 +105,11 @@ using nitrokey::misc::strcpyT; return true; } - std::vector NitrokeyManager::list_devices(){ + std::vector NitrokeyManager::list_devices(){ std::lock_guard lock(mex_dev_com_manager); auto p = make_shared(); - auto device_infos = p->enumerate(); - std::vector strings; - strings.resize(device_infos.size()); - std::transform(device_infos.begin(), device_infos.end(), strings.begin(), - [](auto device_info) { return device_info.m_path; }); - return strings; + return p->enumerate(); } std::vector NitrokeyManager::list_devices_by_cpuID(){ diff --git a/libnitrokey/NitrokeyManager.h b/libnitrokey/NitrokeyManager.h index d6e5df4..6908143 100644 --- a/libnitrokey/NitrokeyManager.h +++ b/libnitrokey/NitrokeyManager.h @@ -80,7 +80,7 @@ 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(); + std::vector list_devices(); std::vector list_devices_by_cpuID(); /** diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index b224653..f9e9ad2 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -90,7 +90,10 @@ TEST_CASE("Use API", "[BASIC]") { REQUIRE(v.size() > 0); for (int i=0; i<10; i++){ - for (auto a : v) { + for (auto i : v) { + if (i.m_deviceModel != DeviceModel::STORAGE) + continue; + auto a = i.m_path; std::cout <<"Connect with: " << a << " " << std::boolalpha << nm->connect_with_path(a) << " "; try{ -- cgit v1.2.1 From 6772dcf38e275b2ab9f962a87e86f89541c13a92 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:03:53 +0100 Subject: Extract vendor and product IDs into constants --- device.cc | 8 ++++++-- libnitrokey/device.h | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/device.cc b/device.cc index 506a68c..74de0b3 100644 --- a/device.cc +++ b/device.cc @@ -38,6 +38,10 @@ using namespace nitrokey::device; using namespace nitrokey::log; using namespace std::chrono; +const uint16_t nitrokey::device::NITROKEY_VID = 0x20a0; +const uint16_t nitrokey::device::NITROKEY_PRO_PID = 0x4108; +const uint16_t nitrokey::device::NITROKEY_STORAGE_PID = 0x4109; + std::atomic_int Device::instances_count{0}; std::chrono::milliseconds Device::default_delay {0} ; @@ -246,14 +250,14 @@ void Device::set_retry_delay(const std::chrono::milliseconds delay){ } Stick10::Stick10(): - Device(0x20a0, 0x4108, DeviceModel::PRO, 100ms, 5, 100ms) + Device(NITROKEY_VID, NITROKEY_PRO_PID, DeviceModel::PRO, 100ms, 5, 100ms) { setDefaultDelay(); } Stick20::Stick20(): - Device(0x20a0, 0x4109, DeviceModel::STORAGE, 40ms, 55, 40ms) + Device(NITROKEY_VID, NITROKEY_STORAGE_PID, DeviceModel::STORAGE, 40ms, 55, 40ms) { setDefaultDelay(); } diff --git a/libnitrokey/device.h b/libnitrokey/device.h index 1a84402..477640f 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -50,6 +50,19 @@ enum class DeviceModel{ STORAGE }; +/** + * The USB vendor ID for Nitrokey devices. + */ +extern const uint16_t NITROKEY_VID; +/** + * The USB product ID for the Nitrokey Pro. + */ +extern const uint16_t NITROKEY_PRO_PID; +/** + * The USB product ID for the Nitrokey Storage. + */ +extern const uint16_t NITROKEY_STORAGE_PID; + /** * Information about a connected device. * -- cgit v1.2.1 From d0d934e8333cffe093569b9f48a10b3500d1ff60 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:04:03 +0100 Subject: Add misc::Option class Option is a simple replacement for std::optional, which was introduced in C++17. It stores a value that can be present or absent. --- libnitrokey/misc.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/libnitrokey/misc.h b/libnitrokey/misc.h index 88254dd..d10c8df 100644 --- a/libnitrokey/misc.h +++ b/libnitrokey/misc.h @@ -29,12 +29,37 @@ #include "log.h" #include "LibraryException.h" #include +#include #include namespace nitrokey { namespace misc { +/** + * Simple replacement for std::optional (C++17). + */ +template +class Option { +public: + Option() : m_hasValue(false), m_value() {} + Option(T value) : m_hasValue(true), m_value(value) {} + + bool has_value() const { + return m_hasValue; + } + T value() const { + if (!m_hasValue) { + throw std::logic_error("Called Option::value without value"); + } + return m_value; + } + +private: + bool m_hasValue; + T m_value; +}; + template std::string toHex(T value){ using namespace std; -- cgit v1.2.1 From 6c52c50d59eafa5acc7ae650695f199b7a014841 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:04:12 +0100 Subject: Add product_id_to_model function --- device.cc | 12 ++++++++++++ libnitrokey/device.h | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/device.cc b/device.cc index 74de0b3..d3f6e09 100644 --- a/device.cc +++ b/device.cc @@ -36,12 +36,24 @@ std::mutex mex_dev_com; using namespace nitrokey::device; using namespace nitrokey::log; +using namespace nitrokey::misc; using namespace std::chrono; const uint16_t nitrokey::device::NITROKEY_VID = 0x20a0; const uint16_t nitrokey::device::NITROKEY_PRO_PID = 0x4108; const uint16_t nitrokey::device::NITROKEY_STORAGE_PID = 0x4109; +Option nitrokey::device::product_id_to_model(uint16_t product_id) { + switch (product_id) { + case NITROKEY_PRO_PID: + return DeviceModel::PRO; + case NITROKEY_STORAGE_PID: + return DeviceModel::STORAGE; + default: + return {}; + } +} + std::atomic_int Device::instances_count{0}; std::chrono::milliseconds Device::default_delay {0} ; diff --git a/libnitrokey/device.h b/libnitrokey/device.h index 477640f..8fbb385 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -26,6 +26,7 @@ #include #include #include +#include "misc.h" #define HID_REPORT_SIZE 65 @@ -63,6 +64,12 @@ extern const uint16_t NITROKEY_PRO_PID; */ extern const uint16_t NITROKEY_STORAGE_PID; +/** + * Convert the given USB product ID to a Nitrokey model. If there is no model + * with that ID, return an absent value. + */ +misc::Option product_id_to_model(uint16_t product_id); + /** * Information about a connected device. * -- cgit v1.2.1 From 1751759356bd64cc78f8f71543c3edd5cdce8376 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:04:22 +0100 Subject: Make Device::enumerate static Device::enumerate does not need any instance data, therefore it is made static. Note that this not only changes the public API by making the method static. We also return all connected Nitrokey devices instead of only Storage devices. The NitrokeyManager method list_devices_by_cpuID is changed to check the device type so that they still only return Storage devices. The list_device method now returns both Storage and Pro devices. --- NitrokeyManager.cc | 10 +++++----- device.cc | 15 ++++++++------- libnitrokey/device.h | 5 ++--- unittest/test_multiple_devices.cc | 8 ++++++-- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 8ca4698..3b57ba6 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -108,8 +108,7 @@ using nitrokey::misc::strcpyT; std::vector NitrokeyManager::list_devices(){ std::lock_guard lock(mex_dev_com_manager); - auto p = make_shared(); - return p->enumerate(); + return Device::enumerate(); } std::vector NitrokeyManager::list_devices_by_cpuID(){ @@ -127,12 +126,13 @@ using nitrokey::misc::strcpyT; LOGD1("Enumerating devices"); std::vector res; - auto d = make_shared(); - const auto v = d->enumerate(); + const auto v = Device::enumerate(); LOGD1("Discovering IDs"); for (auto & i: v){ + if (i.m_deviceModel != DeviceModel::STORAGE) + continue; auto p = i.m_path; - d = make_shared(); + auto d = make_shared(); LOGD1( std::string("Found: ") + p ); d->set_path(p); try{ diff --git a/device.cc b/device.cc index d3f6e09..58dc0e5 100644 --- a/device.cc +++ b/device.cc @@ -188,16 +188,17 @@ int Device::recv(void *packet) { } std::vector Device::enumerate(){ - //TODO make static - auto pInfo = hid_enumerate(m_vid, m_pid); + auto pInfo = hid_enumerate(NITROKEY_VID, 0); auto pInfo_ = pInfo; std::vector res; while (pInfo != nullptr){ - std::string path(pInfo->path); - std::wstring serialNumber(pInfo->serial_number); - auto deviceModel = this->get_device_model(); - DeviceInfo info = { deviceModel, path, serialNumber }; - res.push_back(info); + auto deviceModel = product_id_to_model(pInfo->product_id); + if (deviceModel.has_value()) { + std::string path(pInfo->path); + std::wstring serialNumber(pInfo->serial_number); + DeviceInfo info = { deviceModel.value(), path, serialNumber }; + res.push_back(info); + } pInfo = pInfo->next; } diff --git a/libnitrokey/device.h b/libnitrokey/device.h index 8fbb385..418d335 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -148,12 +148,11 @@ public: */ bool could_be_enumerated(); /** - * Returns a vector with all connected Nitrokey devices of the same device - * type as this device. + * Returns a vector with all connected Nitrokey devices. * * @return information about all connected devices */ - std::vector enumerate(); + static std::vector enumerate(); void show_stats(); diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index f9e9ad2..183af4f 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -35,9 +35,11 @@ using namespace nitrokey; TEST_CASE("List devices", "[BASIC]") { shared_ptr d = make_shared(); - auto v = d->enumerate(); + auto v = Device::enumerate(); REQUIRE(v.size() > 0); for (auto i : v){ + if (i.m_deviceModel != DeviceModel::STORAGE) + continue; auto a = i.m_path; std::cout << a; d->set_path(a); @@ -54,11 +56,13 @@ TEST_CASE("List devices", "[BASIC]") { TEST_CASE("Regenerate AES keys", "[BASIC]") { shared_ptr d = make_shared(); - auto v = d->enumerate(); + auto v = Device::enumerate(); REQUIRE(v.size() > 0); std::vector> devices; for (auto i : v){ + if (i.m_deviceModel != DeviceModel::STORAGE) + continue; auto a = i.m_path; std::cout << a << endl; d = make_shared(); -- cgit v1.2.1 From 14635f49813df1699569d3b13456ea33955de54b Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:04:31 +0100 Subject: Add Device::create static method The method makes it easier to create a std::shared_ptr from a model enum instance. --- device.cc | 11 +++++++++++ libnitrokey/device.h | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/device.cc b/device.cc index 58dc0e5..aabcbfc 100644 --- a/device.cc +++ b/device.cc @@ -209,6 +209,17 @@ std::vector Device::enumerate(){ return res; } +std::shared_ptr Device::create(DeviceModel model) { + switch (model) { + case DeviceModel::PRO: + return std::make_shared(); + case DeviceModel::STORAGE: + return std::make_shared(); + default: + return {}; + } +} + bool Device::could_be_enumerated() { LOG(__FUNCTION__, Loglevel::DEBUG_L2); std::lock_guard lock(mex_dev_com); diff --git a/libnitrokey/device.h b/libnitrokey/device.h index 418d335..eab3888 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -24,6 +24,7 @@ #include #include "hidapi/hidapi.h" #include +#include #include #include #include "misc.h" @@ -154,6 +155,11 @@ public: */ static std::vector enumerate(); + /** + * Create a Device of the given model. + */ + static std::shared_ptr create(DeviceModel model); + void show_stats(); // ErrorCounters get_stats(){ return m_counters; } -- cgit v1.2.1 From 66763febd7990f35d34345175257b2ad9401e829 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:04:42 +0100 Subject: Implement operator<< for DeviceModel --- device.cc | 15 +++++++++++++++ libnitrokey/device.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/device.cc b/device.cc index aabcbfc..35aefca 100644 --- a/device.cc +++ b/device.cc @@ -57,6 +57,21 @@ Option nitrokey::device::product_id_to_model(uint16_t product_id) { std::atomic_int Device::instances_count{0}; std::chrono::milliseconds Device::default_delay {0} ; +std::ostream& nitrokey::device::operator<<(std::ostream& stream, DeviceModel model) { + switch (model) { + case DeviceModel::PRO: + stream << "Pro"; + break; + case DeviceModel::STORAGE: + stream << "Storage"; + break; + default: + stream << "Unknown"; + break; + } + return stream; +} + Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, const milliseconds send_receive_delay, const int retry_receiving_count, const milliseconds retry_timeout) diff --git a/libnitrokey/device.h b/libnitrokey/device.h index eab3888..4b1c239 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "misc.h" @@ -52,6 +53,8 @@ enum class DeviceModel{ STORAGE }; +std::ostream& operator<<(std::ostream& stream, DeviceModel model); + /** * The USB vendor ID for Nitrokey devices. */ -- cgit v1.2.1 From 4f7c1b31191d98904276fecd236e6b68b405c349 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:04:51 +0100 Subject: Add test case for Device::enumerate to test_multiple_devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current test case is renamed to “List Storage devices” as it also displays the SD card ID. The new test case, “List devices”, lists all connected devices and prints their model, path and serial number. --- unittest/test_multiple_devices.cc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index 183af4f..2ea8a20 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -34,6 +34,28 @@ using namespace nitrokey; TEST_CASE("List devices", "[BASIC]") { + auto v = Device::enumerate(); + REQUIRE(v.size() > 0); + for (auto i : v){ + auto d = Device::create(i.m_deviceModel); + if (!d) { + std::cout << "Could not create device with model " << i.m_deviceModel << "\n"; + continue; + } + std::cout << i.m_deviceModel << " " << i.m_path << " "; + std::wcout << i.m_serialNumber; + std::cout << " |"; + d->set_path(i.m_path); + d->connect(); + auto res = GetStatus::CommandTransaction::run(d); + std::cout << " " << res.data().card_serial_u32 << " " + << res.data().get_card_serial_hex() + << std::endl; + d->disconnect(); + } +} + +TEST_CASE("List Storage devices", "[BASIC]") { shared_ptr d = make_shared(); auto v = Device::enumerate(); REQUIRE(v.size() > 0); -- cgit v1.2.1 From 8979b7301f79e167a8060772cf83913703f70f2c Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:05:00 +0100 Subject: Change NitrokeyManager::connect_with_path to also work with Pro Previously, Stick20 was hardcoded in connect_with_path. Now we first use hid_enumerate to find out the model on that path, then we connect to that model. We also could have added the model as a parameter to connect_with_path. Yet we cannot directly check the model after connecting, so this would be error-prone. --- NitrokeyManager.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 3b57ba6..99f0b7a 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -29,6 +29,7 @@ #include "libnitrokey/misc.h" #include #include "libnitrokey/cxx_semantics.h" +#include "libnitrokey/misc.h" #include #include @@ -216,7 +217,26 @@ using nitrokey::misc::strcpyT; } } - auto p = make_shared(); + auto info_ptr = hid_enumerate(NITROKEY_VID, 0); + auto first_info_ptr = info_ptr; + if (!info_ptr) + return false; + + misc::Option model; + while (info_ptr && !model.has_value()) { + if (path == std::string(info_ptr->path)) { + model = product_id_to_model(info_ptr->product_id); + } + info_ptr = info_ptr->next; + } + hid_free_enumeration(first_info_ptr); + + if (!model.has_value()) + return false; + + auto p = Device::create(model.value()); + if (!p) + return false; p->set_path(path); if(!p->connect()) return false; -- cgit v1.2.1 From a63968570dc99ddf193589270218e6c6df34c460 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:05:09 +0100 Subject: Add test case for NitrokeyManager::connect_with_path to test_multiple_devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current test case is renamed to “Use Storage API” as it queries the storage status. The new test case, “Use API”, lists all connected devices and prints their model, path and serial number. --- unittest/test_multiple_devices.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index 2ea8a20..d644cfd 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -115,6 +115,29 @@ TEST_CASE("Use API", "[BASIC]") { auto v = nm->list_devices(); REQUIRE(v.size() > 0); + for (auto i : v) { + std::cout << "Connect with: " << i.m_deviceModel << " " << i.m_path << " "; + std::wcout << i.m_serialNumber; + std::cout << " | " << std::boolalpha << nm->connect_with_path(i.m_path) << " |"; + try { + auto status = nm->get_status(); + std::cout << " " << status.card_serial_u32 << " " + << status.get_card_serial_hex() + << std::endl; + } catch (const LongOperationInProgressException &e) { + std::cout << "long operation in progress on " << i.m_path + << " " << std::to_string(e.progress_bar_value) << std::endl; + } + } +} + + +TEST_CASE("Use Storage 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 i : v) { if (i.m_deviceModel != DeviceModel::STORAGE) -- cgit v1.2.1 From 2c79c15dc9aa4ec7eca454b793bf43a9a3ba85db Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:05:18 +0100 Subject: Add NK_connect_with_path to C API NK_connect_with_path corresponds to NitrokeyManager::connect_with_path. --- NK_C_API.cc | 8 ++++++++ NK_C_API.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/NK_C_API.cc b/NK_C_API.cc index 7d0a10e..f0c23a1 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -743,6 +743,14 @@ NK_C_API char* NK_get_SD_usage_data_as_string() { }); } + NK_C_API int NK_connect_with_path(const char* path) { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->connect_with_path(path) ? 1 : 0; + }); + } + + NK_C_API int NK_wink() { auto m = NitrokeyManager::instance(); return get_without_result([&]() { diff --git a/NK_C_API.h b/NK_C_API.h index b1bdf1e..81da956 100644 --- a/NK_C_API.h +++ b/NK_C_API.h @@ -779,6 +779,14 @@ extern "C" { */ NK_C_API int NK_connect_with_ID(const char* id); + /** + * Connects to a device with the given path. The path is a USB device + * path as returned by hidapi. + * @param path the device path + * @return 1 on successful connection, 0 otherwise + */ + NK_C_API int NK_connect_with_path(const char* path); + /** * Blink red and green LED alternatively and infinitely (until device is reconnected). * @return command processing error code -- cgit v1.2.1 From a80378e0c770a503ddaafc0c7aacb78cac667b8f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:05:28 +0100 Subject: Change std::wstring to std::string in DeviceInfo For easier handling, we should use a std::string instead of std::wstring for the serial number in DeviceInfo. For the conversion, I assume that the serial number is valid UTF-8. As it should be alphanumeric and ASCII only, this should be true. --- device.cc | 6 +++++- libnitrokey/device.h | 2 +- unittest/test_multiple_devices.cc | 10 ++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/device.cc b/device.cc index 35aefca..bc42965 100644 --- a/device.cc +++ b/device.cc @@ -20,7 +20,9 @@ */ #include +#include #include +#include #include #include #include @@ -210,7 +212,9 @@ std::vector Device::enumerate(){ auto deviceModel = product_id_to_model(pInfo->product_id); if (deviceModel.has_value()) { std::string path(pInfo->path); - std::wstring serialNumber(pInfo->serial_number); + std::wstring serialNumberW(pInfo->serial_number); + std::wstring_convert> converter; + std::string serialNumber = converter.to_bytes(serialNumberW); DeviceInfo info = { deviceModel.value(), path, serialNumber }; res.push_back(info); } diff --git a/libnitrokey/device.h b/libnitrokey/device.h index 4b1c239..d50080d 100644 --- a/libnitrokey/device.h +++ b/libnitrokey/device.h @@ -92,7 +92,7 @@ struct DeviceInfo { /** * The serial number of the device. */ - std::wstring m_serialNumber; + std::string m_serialNumber; }; #include diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index d644cfd..bc1b60b 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -42,9 +42,8 @@ TEST_CASE("List devices", "[BASIC]") { std::cout << "Could not create device with model " << i.m_deviceModel << "\n"; continue; } - std::cout << i.m_deviceModel << " " << i.m_path << " "; - std::wcout << i.m_serialNumber; - std::cout << " |"; + std::cout << i.m_deviceModel << " " << i.m_path << " " + << i.m_serialNumber << " |"; d->set_path(i.m_path); d->connect(); auto res = GetStatus::CommandTransaction::run(d); @@ -116,9 +115,8 @@ TEST_CASE("Use API", "[BASIC]") { REQUIRE(v.size() > 0); for (auto i : v) { - std::cout << "Connect with: " << i.m_deviceModel << " " << i.m_path << " "; - std::wcout << i.m_serialNumber; - std::cout << " | " << std::boolalpha << nm->connect_with_path(i.m_path) << " |"; + std::cout << "Connect with: " << i.m_deviceModel << " " << i.m_path << " " + << i.m_serialNumber << " | " << std::boolalpha << nm->connect_with_path(i.m_path) << " |"; try { auto status = nm->get_status(); std::cout << " " << status.card_serial_u32 << " " -- cgit v1.2.1 From 5d94dece0392ce0d5486097abf8918b6922f85d2 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:05:37 +0100 Subject: Add NK_device_info, NK_list_devices and NK_free_device_info NK_list_devices corresponds to NitrokeyManager::list_devices. It returns a linked list of NK_device_info, which has to be freed using the NK_free_device_info function. --- NK_C_API.cc | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ NK_C_API.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/NK_C_API.cc b/NK_C_API.cc index f0c23a1..05457d0 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -736,6 +736,66 @@ NK_C_API char* NK_get_SD_usage_data_as_string() { }); } + bool copy_device_info(const DeviceInfo& source, NK_device_info* target) { + switch (source.m_deviceModel) { + case DeviceModel::PRO: + target->model = NK_PRO; + break; + case DeviceModel::STORAGE: + target->model = NK_STORAGE; + break; + default: + return false; + } + + target->path = strndup(source.m_path.c_str(), MAXIMUM_STR_REPLY_LENGTH); + target->serial_number = strndup(source.m_serialNumber.c_str(), MAXIMUM_STR_REPLY_LENGTH); + target->next = nullptr; + + return target->path && target->serial_number; + } + + NK_C_API struct NK_device_info* NK_list_devices() { + auto nm = NitrokeyManager::instance(); + return get_with_result([&]() -> NK_device_info* { + auto v = nm->list_devices(); + if (v.empty()) + return nullptr; + + auto result = new NK_device_info(); + auto ptr = result; + auto first = v.begin(); + if (!copy_device_info(*first, ptr)) { + NK_free_device_info(result); + return nullptr; + } + v.erase(first); + + for (auto& info : v) { + ptr->next = new NK_device_info(); + ptr = ptr->next; + + if (!copy_device_info(info, ptr)) { + NK_free_device_info(result); + return nullptr; + } + } + return result; + }); + } + + NK_C_API void NK_free_device_info(struct NK_device_info* device_info) { + if (!device_info) + return; + + if (device_info->next) + NK_free_device_info(device_info->next); + + free(device_info->path); + free(device_info->serial_number); + delete device_info; + } + NK_C_API int NK_connect_with_ID(const char* id) { auto m = NitrokeyManager::instance(); return get_with_result([&]() { diff --git a/NK_C_API.h b/NK_C_API.h index 81da956..f17098a 100644 --- a/NK_C_API.h +++ b/NK_C_API.h @@ -57,6 +57,29 @@ extern "C" { NK_STORAGE = 2 }; + /** + * The connection info for a Nitrokey device as a linked list. + */ + struct NK_device_info { + /** + * The model of the Nitrokey device. + */ + enum NK_device_model model; + /** + * The USB device path for NK_connect_with_path. + */ + char* path; + /** + * The serial number. + */ + char* serial_number; + /** + * The pointer to the next element of the linked list or null + * if this is the last element in the list. + */ + struct NK_device_info* next; + }; + /** * Stores the status of a Storage device. */ @@ -768,6 +791,19 @@ extern "C" { */ NK_C_API char* NK_list_devices_by_cpuID(); + /** + * Returns a linked list of all connected devices, or null if no devices + * are connected or an error occured. The linked list must be freed by + * calling NK_free_device_info. + * @return a linked list of all connected devices + */ + NK_C_API struct NK_device_info* NK_list_devices(); + + /** + * Free a linked list returned by NK_list_devices. + * @param the linked list to free or null + */ + NK_C_API void NK_free_device_info(struct NK_device_info* device_info); /** * Connects to the device with given ID. ID's list could be created with NK_list_devices_by_cpuID. -- cgit v1.2.1 From cf32902131d4f7bd68622ca9d243fdff5a5ed519 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:05:55 +0100 Subject: Add simple test for NK_list_devices Unfortunately, I cannot test more as the current ffi implementation does not allow me to import struct definitions. Without the definition for the NK_device_info struct, I cannot inspect the result of the NK_list_devices function. --- unittest/test_multiple.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/unittest/test_multiple.py b/unittest/test_multiple.py index 3f1d2d5..902234f 100644 --- a/unittest/test_multiple.py +++ b/unittest/test_multiple.py @@ -29,11 +29,19 @@ from tqdm import tqdm from conftest import skip_if_device_version_lower_than from constants import DefaultPasswords, DeviceErrorCode, bb -from misc import gs, wait +from misc import gs, wait, ffi pprint = pprint.PrettyPrinter(indent=4).pprint +@pytest.mark.other +@pytest.mark.info +def test_list_devices(C): + infos = C.NK_list_devices() + assert infos != ffi.NULL + C.NK_free_device_info(infos) + + @pytest.mark.other @pytest.mark.info def test_get_status_storage_multiple(C): -- cgit v1.2.1 From e9cfa720d39e0c540fe530f907a84e9a4b5d1240 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:06:16 +0100 Subject: Add test for NK_connect_with_path As we cannot read the output of NK_list_devices in the Python tests at the moment, this test case uses NK_list_devices_by_cpuID instead. --- unittest/test_multiple.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/unittest/test_multiple.py b/unittest/test_multiple.py index 902234f..821a3b7 100644 --- a/unittest/test_multiple.py +++ b/unittest/test_multiple.py @@ -42,6 +42,25 @@ def test_list_devices(C): C.NK_free_device_info(infos) +@pytest.mark.other +@pytest.mark.info +def test_connect_with_path(C): + ids = gs(C.NK_list_devices_by_cpuID()) + # NK_list_devices_by_cpuID already opened the devices, so we have to close + # them before trying to reconnect + assert C.NK_logout() == 0 + + devices_list = ids.split(b';') + for value in devices_list: + parts = value.split(b'_p_') + assert len(parts) < 3 + if len(parts) == 2: + path = parts[1] + else: + path = parts[0] + assert C.NK_connect_with_path(path) == 1 + + @pytest.mark.other @pytest.mark.info def test_get_status_storage_multiple(C): -- cgit v1.2.1 From cd1cfdbfc4113186f80dbadf5eb76543b22e34bd Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 13 Jan 2019 12:29:58 +0100 Subject: Add test for NK_list_devices to test_multiple_devices As we cannot test this function properly in Python (due to missing struct definitions), we test it in the C++ test suite. --- unittest/test_multiple_devices.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/unittest/test_multiple_devices.cc b/unittest/test_multiple_devices.cc index bc1b60b..4b1e2c1 100644 --- a/unittest/test_multiple_devices.cc +++ b/unittest/test_multiple_devices.cc @@ -29,6 +29,7 @@ const char * RFC_SECRET = "12345678901234567890"; #include #include #include +#include "../NK_C_API.h" using namespace nitrokey; @@ -108,6 +109,24 @@ TEST_CASE("Regenerate AES keys", "[BASIC]") { } +TEST_CASE("Use C API", "[BASIC]") { + auto ptr = NK_list_devices(); + auto first_ptr = ptr; + REQUIRE(ptr != nullptr); + + while (ptr) { + std::cout << "Connect with: " << ptr->model << " " << ptr->path << " " + << ptr->serial_number << " | " << NK_connect_with_path(ptr->path) << " | "; + auto status = NK_status(); + std::cout << status << std::endl; + free(status); + ptr = ptr->next; + } + + NK_free_device_info(first_ptr); +} + + TEST_CASE("Use API", "[BASIC]") { auto nm = NitrokeyManager::instance(); nm->set_loglevel(2); -- cgit v1.2.1