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 #ifndef DEVICE_H
23 #define DEVICE_H
24 #include <chrono>
25 #include "hidapi/hidapi.h"
26 #include <cstdint>
27 #include <memory>
28 #include <string>
29 #include <ostream>
30 #include <vector>
31 #include "misc.h"
32 
33 #define HID_REPORT_SIZE 65
34 
35 #include <atomic>
36 
37 namespace nitrokey {
38 namespace device {
39     using namespace std::chrono_literals;
40     using std::chrono::milliseconds;
41 
42     struct EnumClassHash
43     {
44         template <typename T>
operatorEnumClassHash45         std::size_t operator()(T t) const
46         {
47           return static_cast<std::size_t>(t);
48         }
49     };
50 
51 enum class DeviceModel{
52     PRO,
53     STORAGE,
54     LIBREM
55 };
56 
57 std::ostream& operator<<(std::ostream& stream, DeviceModel model);
58 
59 /**
60  * The USB vendor ID for Nitrokey devices.
61  */
62 extern const uint16_t NITROKEY_VID;
63 /**
64  * The USB product ID for the Nitrokey Pro.
65  */
66 extern const uint16_t NITROKEY_PRO_PID;
67 /**
68  * The USB product ID for the Nitrokey Storage.
69  */
70 extern const uint16_t NITROKEY_STORAGE_PID;
71 /**
72  * The USB vendor ID for Purism devices.
73  */
74 extern const uint16_t PURISM_VID;
75 /**
76  * The USB product ID for the Librem Key.
77  */
78 extern const uint16_t LIBREM_KEY_PID;
79 
80 /**
81  * Convert the given USB product ID to a Nitrokey model.  If there is no model
82  * with that ID, return an absent value.
83  */
84 misc::Option<DeviceModel> product_id_to_model(uint16_t product_id);
85 misc::Option<DeviceModel> product_id_to_model(uint16_t vendor_id, uint16_t product_id);
86 
87 /**
88  * Information about a connected device.
89  *
90  * This struct contains the information about a connected device returned by
91  * hidapi when enumerating the connected devices.
92  */
93 struct DeviceInfo {
94     /**
95      * The model of the connected device.
96      */
97     DeviceModel m_deviceModel;
98     /**
99      * The USB connection path for the device.
100      */
101     std::string m_path;
102     /**
103      * The serial number of the device.
104      */
105     std::string m_serialNumber;
106 };
107 
108 #include <atomic>
109 
110 class Device {
111 
112 public:
113 
114   struct ErrorCounters{
115     using cnt = std::atomic_int;
116     cnt wrong_CRC;
117     cnt CRC_other_than_awaited;
118     cnt busy;
119     cnt total_retries;
120     cnt sending_error;
121     cnt receiving_error;
122     cnt total_comm_runs;
123     cnt successful_storage_commands;
124     cnt command_successful_recv;
125     cnt recv_executed;
126     cnt sends_executed;
127     cnt busy_progressbar;
128     cnt command_result_not_equal_0_recv;
129     cnt communication_successful;
130     cnt low_level_reconnect;
131     std::string get_as_string();
132 
133   } m_counters = {};
134 
135 
136     Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
137                    const milliseconds send_receive_delay, const int retry_receiving_count,
138                    const milliseconds retry_timeout);
139 
140     virtual ~Device();
141 
142   // lack of device is not actually an error,
143   // so it doesn't throw
144   virtual bool connect();
145   virtual bool disconnect();
146 
147   /*
148    *	Sends packet of HID_REPORT_SIZE.
149    */
150   virtual int send(const void *packet);
151 
152   /*
153    *	Gets packet of HID_REPORT_SIZE.
154    *	Can sleep. See below.
155    */
156   virtual int recv(void *packet);
157 
158   /***
159    * Returns true if some device is visible by OS with given VID and PID
160    * whether the device is connected through HID API or not.
161    * @return true if visible by OS
162    */
163   bool could_be_enumerated();
164   /**
165    * Returns a vector with all connected Nitrokey devices.
166    *
167    * @return information about all connected devices
168    */
169   static std::vector<DeviceInfo> enumerate();
170 
171   /**
172    * Create a Device of the given model.
173    */
174   static std::shared_ptr<Device> create(DeviceModel model);
175 
176 
177         void show_stats();
178 //  ErrorCounters get_stats(){ return m_counters; }
get_retry_receiving_count()179   int get_retry_receiving_count() const { return m_retry_receiving_count; };
get_retry_sending_count()180   int get_retry_sending_count() const { return m_retry_sending_count; };
get_retry_timeout()181   std::chrono::milliseconds get_retry_timeout() const { return m_retry_timeout; };
get_send_receive_delay()182   std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;}
183 
get_last_command_status()184   int get_last_command_status() {int a = std::atomic_exchange(&last_command_status, static_cast<uint8_t>(0)); return a;};
set_last_command_status(uint8_t _err)185   void set_last_command_status(uint8_t _err) { last_command_status = _err;} ;
last_command_sucessfull()186   bool last_command_sucessfull() const {return last_command_status == 0;};
get_device_model()187   DeviceModel get_device_model() const {return m_model;}
188   void set_receiving_delay(std::chrono::milliseconds delay);
189   void set_retry_delay(std::chrono::milliseconds delay);
190   static void set_default_device_speed(int delay);
191   void setDefaultDelay();
192   void set_path(const std::string path);
193 
194 
195         private:
196   std::atomic<uint8_t> last_command_status;
197   void _reconnect();
198   bool _connect();
199   bool _disconnect();
200 
201 protected:
202   const uint16_t m_vid;
203   const uint16_t m_pid;
204   const DeviceModel m_model;
205 
206   /*
207    *	While the project uses Signal11 portable HIDAPI
208    *	library, there's no way of doing it asynchronously,
209    *	hence polling.
210    */
211   const int m_retry_sending_count;
212   const int m_retry_receiving_count;
213   std::chrono::milliseconds m_retry_timeout;
214   std::chrono::milliseconds m_send_receive_delay;
215   std::atomic<hid_device *>mp_devhandle;
216   std::string m_path;
217 
218   static std::atomic_int instances_count;
219   static std::chrono::milliseconds default_delay ;
220 };
221 
222 class Stick10 : public Device {
223  public:
224   Stick10();
225 
226 };
227 
228 class Stick20 : public Device {
229  public:
230   Stick20();
231 };
232 
233 class LibremKey : public Device {
234  public:
235   LibremKey();
236 };
237 
238 }
239 }
240 #endif
241