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