1 /*
2 * Copyright (c) 2015-2018 Nitrokey UG
3 *
4 * This file is part of libnitrokey.
5 *
6 * libnitrokey is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * any later version.
10 *
11 * libnitrokey is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * SPDX-License-Identifier: LGPL-3.0
20 */
21
22 #include <chrono>
23 #include <codecvt>
24 #include <iostream>
25 #include <locale>
26 #include <thread>
27 #include <cstddef>
28 #include <stdexcept>
29 #include "hidapi/hidapi.h"
30 #include "libnitrokey/misc.h"
31 #include "libnitrokey/device.h"
32 #include "libnitrokey/log.h"
33 #include <mutex>
34 #include "DeviceCommunicationExceptions.h"
35 #include "device.h"
36
37 std::mutex mex_dev_com;
38
39 using namespace nitrokey::device;
40 using namespace nitrokey::log;
41 using namespace nitrokey::misc;
42 using namespace std::chrono;
43
44 const uint16_t nitrokey::device::NITROKEY_VID = 0x20a0;
45 const uint16_t nitrokey::device::NITROKEY_PRO_PID = 0x4108;
46 const uint16_t nitrokey::device::NITROKEY_STORAGE_PID = 0x4109;
47
48 const uint16_t nitrokey::device::PURISM_VID = 0x316d;
49 const uint16_t nitrokey::device::LIBREM_KEY_PID = 0x4c4b;
50
product_id_to_model(uint16_t product_id)51 Option<DeviceModel> nitrokey::device::product_id_to_model(uint16_t product_id) {
52 return product_id_to_model(NITROKEY_VID, product_id);
53 }
54
product_id_to_model(uint16_t vendor_id,uint16_t product_id)55 Option<DeviceModel> nitrokey::device::product_id_to_model(uint16_t vendor_id, uint16_t product_id) {
56 switch (vendor_id) {
57 case NITROKEY_VID:
58 switch (product_id) {
59 case NITROKEY_PRO_PID:
60 return DeviceModel::PRO;
61 case NITROKEY_STORAGE_PID:
62 return DeviceModel::STORAGE;
63 default:
64 return {};
65 }
66 case PURISM_VID:
67 switch (product_id) {
68 case LIBREM_KEY_PID:
69 return DeviceModel::LIBREM;
70 default:
71 return {};
72 }
73 default:
74 return {};
75 }
76 }
77
78 std::atomic_int Device::instances_count{0};
79 std::chrono::milliseconds Device::default_delay {0} ;
80
operator <<(std::ostream & stream,DeviceModel model)81 std::ostream& nitrokey::device::operator<<(std::ostream& stream, DeviceModel model) {
82 switch (model) {
83 case DeviceModel::PRO:
84 stream << "Pro";
85 break;
86 case DeviceModel::STORAGE:
87 stream << "Storage";
88 break;
89 case DeviceModel::LIBREM:
90 stream << "Librem";
91 break;
92 default:
93 stream << "Unknown";
94 break;
95 }
96 return stream;
97 }
98
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)99 Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
100 const milliseconds send_receive_delay, const int retry_receiving_count,
101 const milliseconds retry_timeout)
102 :
103 last_command_status(0),
104 m_vid(vid),
105 m_pid(pid),
106 m_model(model),
107 m_retry_sending_count(1),
108 m_retry_receiving_count(retry_receiving_count),
109 m_retry_timeout(retry_timeout),
110 m_send_receive_delay(send_receive_delay),
111 mp_devhandle(nullptr)
112 {
113 instances_count++;
114 }
115
disconnect()116 bool Device::disconnect() {
117 //called in object's destructor
118 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
119 std::lock_guard<std::mutex> lock(mex_dev_com);
120 return _disconnect();
121 }
122
_disconnect()123 bool Device::_disconnect() {
124 LOG(std::string(__FUNCTION__) +
125 std::string(m_model == DeviceModel::PRO ? "PRO" : (m_model == DeviceModel::STORAGE ? "STORAGE" : "LIBREM")),
126 Loglevel::DEBUG_L2);
127 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
128
129 if(mp_devhandle == nullptr) {
130 LOG(std::string("Disconnection: handle already freed: ") + std::to_string(mp_devhandle == nullptr) + " ("+m_path+")", Loglevel::DEBUG_L1);
131 return false;
132 }
133
134 hid_close(mp_devhandle);
135 mp_devhandle = nullptr;
136 #ifndef __APPLE__
137 if (instances_count == 1){
138 LOG(std::string("Calling hid_exit"), Loglevel::DEBUG_L2);
139 hid_exit();
140 }
141 #endif
142 return true;
143 }
144
connect()145 bool Device::connect() {
146 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
147 std::lock_guard<std::mutex> lock(mex_dev_com);
148 return _connect();
149 }
150
_connect()151 bool Device::_connect() {
152 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
153
154 // hid_init(); // done automatically on hid_open
155 if (m_path.empty()){
156 mp_devhandle = hid_open(m_vid, m_pid, nullptr);
157 } else {
158 mp_devhandle = hid_open_path(m_path.c_str());
159 }
160 const bool success = mp_devhandle != nullptr;
161 LOG(std::string("Connection success: ") + std::to_string(success) + " ("+m_path+")", Loglevel::DEBUG_L1);
162 return success;
163 }
164
set_path(const std::string path)165 void Device::set_path(const std::string path){
166 m_path = path;
167 }
168
send(const void * packet)169 int Device::send(const void *packet) {
170 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
171 std::lock_guard<std::mutex> lock(mex_dev_com);
172 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
173
174 int send_feature_report = -1;
175
176 for (int i = 0; i < 3 && send_feature_report < 0; ++i) {
177 if (mp_devhandle == nullptr) {
178 LOG(std::string("Connection fail") , Loglevel::DEBUG_L2);
179 throw DeviceNotConnected("Attempted HID send on an invalid descriptor.");
180 }
181 send_feature_report = hid_send_feature_report(
182 mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE);
183 if (send_feature_report < 0) _reconnect();
184 //add thread sleep?
185 LOG(std::string("Sending attempt: ")+std::to_string(i+1) + " / 3" , Loglevel::DEBUG_L2);
186 }
187 return send_feature_report;
188 }
189
recv(void * packet)190 int Device::recv(void *packet) {
191 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
192 std::lock_guard<std::mutex> lock(mex_dev_com);
193 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
194 int status;
195 int retry_count = 0;
196
197 for (;;) {
198 if (mp_devhandle == nullptr){
199 LOG(std::string("Connection fail") , Loglevel::DEBUG_L2);
200 throw DeviceNotConnected("Attempted HID receive on an invalid descriptor.");
201 }
202
203 status = (hid_get_feature_report(mp_devhandle, (unsigned char *)(packet),
204 HID_REPORT_SIZE));
205
206 auto pwherr = hid_error(mp_devhandle);
207 std::wstring wherr = (pwherr != nullptr) ? pwherr : L"No error message";
208 std::string herr(wherr.begin(), wherr.end());
209 LOG(std::string("libhid error message: ") + herr,
210 Loglevel::DEBUG_L2);
211
212 if (status > 0) break; // success
213 if (retry_count++ >= m_retry_receiving_count) {
214 LOG(
215 "Maximum retry count reached: " + std::to_string(retry_count),
216 Loglevel::WARNING);
217 LOG(
218 std::string("Counter stats: ") + m_counters.get_as_string(),
219 Loglevel::DEBUG);
220 break;
221 }
222 _reconnect();
223 LOG("Retrying... " + std::to_string(retry_count),
224 Loglevel::DEBUG);
225 std::this_thread::sleep_for(m_retry_timeout);
226 }
227
228 return status;
229 }
230
231 namespace {
add_vendor_devices(std::vector<DeviceInfo> & res,uint16_t vendor_id)232 void add_vendor_devices(std::vector<DeviceInfo>& res, uint16_t vendor_id){
233 auto pInfo = hid_enumerate(vendor_id, 0);
234 auto pInfo_ = pInfo;
235 while (pInfo != nullptr){
236 if (pInfo->path == nullptr || pInfo->serial_number == nullptr) {
237 continue;
238 }
239 auto deviceModel = product_id_to_model(vendor_id, pInfo->product_id);
240 if (deviceModel.has_value()) {
241 std::string path(pInfo->path);
242 std::wstring serialNumberW(pInfo->serial_number);
243 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
244 std::string serialNumber = converter.to_bytes(serialNumberW);
245 DeviceInfo info = { deviceModel.value(), path, serialNumber };
246 res.push_back(info);
247 }
248 pInfo = pInfo->next;
249 }
250
251 if (pInfo_ != nullptr){
252 hid_free_enumeration(pInfo_);
253 }
254 }
255 }
256
enumerate()257 std::vector<DeviceInfo> Device::enumerate(){
258 std::vector<DeviceInfo> res;
259 ::add_vendor_devices(res, NITROKEY_VID);
260 ::add_vendor_devices(res, PURISM_VID);
261 return res;
262 }
263
create(DeviceModel model)264 std::shared_ptr<Device> Device::create(DeviceModel model) {
265 switch (model) {
266 case DeviceModel::PRO:
267 return std::make_shared<Stick10>();
268 case DeviceModel::STORAGE:
269 return std::make_shared<Stick20>();
270 case DeviceModel::LIBREM:
271 return std::make_shared<LibremKey>();
272 default:
273 return {};
274 }
275 }
276
could_be_enumerated()277 bool Device::could_be_enumerated() {
278 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
279 std::lock_guard<std::mutex> lock(mex_dev_com);
280 if (mp_devhandle==nullptr){
281 return false;
282 }
283 #ifndef __APPLE__
284 auto pInfo = hid_enumerate(m_vid, m_pid);
285 if (pInfo != nullptr){
286 hid_free_enumeration(pInfo);
287 return true;
288 }
289 return false;
290 #else
291 // alternative for OSX
292 unsigned char buf[1];
293 return hid_read_timeout(mp_devhandle, buf, sizeof(buf), 20) != -1;
294 #endif
295 }
296
show_stats()297 void Device::show_stats() {
298 auto s = m_counters.get_as_string();
299 LOG(s, Loglevel::DEBUG_L2);
300 }
301
_reconnect()302 void Device::_reconnect() {
303 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
304 ++m_counters.low_level_reconnect;
305 _disconnect();
306 _connect();
307 }
308
~Device()309 Device::~Device() {
310 show_stats();
311 disconnect();
312 instances_count--;
313 }
314
set_default_device_speed(int delay)315 void Device::set_default_device_speed(int delay) {
316 default_delay = std::chrono::duration<int, std::milli>(delay);
317 }
318
319
set_receiving_delay(const std::chrono::milliseconds delay)320 void Device::set_receiving_delay(const std::chrono::milliseconds delay){
321 std::lock_guard<std::mutex> lock(mex_dev_com);
322 m_send_receive_delay = delay;
323 }
324
set_retry_delay(const std::chrono::milliseconds delay)325 void Device::set_retry_delay(const std::chrono::milliseconds delay){
326 std::lock_guard<std::mutex> lock(mex_dev_com);
327 m_retry_timeout = delay;
328 }
329
Stick10()330 Stick10::Stick10():
331 Device(NITROKEY_VID, NITROKEY_PRO_PID, DeviceModel::PRO, 100ms, 5, 100ms)
332 {
333 setDefaultDelay();
334 }
335
336
Stick20()337 Stick20::Stick20():
338 Device(NITROKEY_VID, NITROKEY_STORAGE_PID, DeviceModel::STORAGE, 40ms, 55, 40ms)
339 {
340 setDefaultDelay();
341 }
342
343
LibremKey()344 LibremKey::LibremKey():
345 Device(PURISM_VID, LIBREM_KEY_PID, DeviceModel::LIBREM, 100ms, 5, 100ms)
346 {
347 setDefaultDelay();
348 }
349
350 #include <sstream>
351 #define p(x) ss << #x << " " << x << ", ";
get_as_string()352 std::string Device::ErrorCounters::get_as_string() {
353 #ifdef NO_LOG
354 return "";
355 #endif
356
357 std::stringstream ss;
358 p(total_comm_runs);
359 p(communication_successful);
360 ss << "(";
361 p(command_successful_recv);
362 p(command_result_not_equal_0_recv);
363 ss << "), ";
364 p(sends_executed);
365 p(recv_executed);
366 p(successful_storage_commands);
367 p(total_retries);
368 ss << "(";
369 p(busy);
370 p(busy_progressbar);
371 p(CRC_other_than_awaited);
372 p(wrong_CRC);
373 ss << "), ";
374 p(low_level_reconnect);
375 p(sending_error);
376 p(receiving_error);
377 return ss.str();
378 }
379
setDefaultDelay()380 void Device::setDefaultDelay() {
381 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
382
383 auto count = default_delay.count();
384 if (count != 0){
385 LOG("Setting default delay to " + std::to_string(count), Loglevel::DEBUG_L2);
386 m_retry_timeout = default_delay;
387 m_send_receive_delay = default_delay;
388 }
389 }
390
391 #undef p
392