1 /*
2  * Copyright (c) 2017-2018 Nitrokey UG
3  *
4  * This file is part of Nitrokey App.
5  *
6  * Nitrokey App is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * any later version.
10  *
11  * Nitrokey App 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 General Public License
17  * along with Nitrokey App. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * SPDX-License-Identifier: GPL-3.0
20  */
21 
22 #include <libnitrokey/NitrokeyManager.h>
23 #include "libada.h"
24 
25 
26 std::shared_ptr <libada> libada::_instance = nullptr;
27 
28 using nm = nitrokey::NitrokeyManager;
29 
i()30 std::shared_ptr<libada> libada::i() {
31   if (_instance == nullptr){
32     _instance = std::make_shared<libada>();
33     nm::instance()->set_loglevel(nitrokey::Loglevel::DEBUG_L2);
34   }
35   return _instance;
36 }
37 
libada()38 libada::libada() {
39   cache_TOTP_name.setGetter([](const int i) -> const char *{
40     return nm::instance()->get_totp_slot_name(i);
41   });
42   cache_HOTP_name.setGetter([](const int i) -> const char *{
43     return nm::instance()->get_hotp_slot_name(i);
44   });
45   cache_PWS_name.setGetter([](const int i) -> const char *{
46     return nm::instance()->get_password_safe_slot_name(i);
47   });
48 
49 }
50 
~libada()51 libada::~libada() {}
52 
getMajorFirmwareVersion()53 int libada::getMajorFirmwareVersion() {
54   if (major_firmware_version_cached != invalid_value)
55     return major_firmware_version_cached;
56   try{
57     major_firmware_version_cached = nm::instance()->get_major_firmware_version();
58     return major_firmware_version_cached;
59   }
60   catch (DeviceCommunicationException &e){
61   }
62   return invalid_value;
63 }
64 
getMinorFirmwareVersion()65 int libada::getMinorFirmwareVersion() {
66   if (minor_firmware_version_cached != invalid_value)
67     return minor_firmware_version_cached;
68   try{
69     minor_firmware_version_cached = nm::instance()->get_minor_firmware_version();
70     return minor_firmware_version_cached;
71   }
72   catch (DeviceCommunicationException &e){
73   }
74   return invalid_value;
75 }
76 
getAdminPasswordRetryCount()77 int libada::getAdminPasswordRetryCount() {
78   try{
79     if (nm::instance()->is_connected())
80       return nm::instance()->get_admin_retry_count();
81   }
82   catch (DeviceCommunicationException &e){
83   }
84   return invalid_value;
85 
86 }
87 
getUserPasswordRetryCount()88 int libada::getUserPasswordRetryCount() {
89   try{
90     if (nm::instance()->is_connected()){
91       return nm::instance()->get_user_retry_count();
92     }
93   }
94   catch (DeviceCommunicationException &e){
95   }
96   return invalid_value;
97 }
98 
getCardSerial()99 std::string libada::getCardSerial() {
100   try {
101     return nm::instance()->get_serial_number();
102   }
103   catch (DeviceCommunicationException &e){
104   }
105   return "--error--";
106 }
107 
108 #include <QtCore/QMutex>
109 #include <libnitrokey/CommandFailedException.h>
110 #include "hotpslot.h"
111 
on_FactoryReset()112 void libada::on_FactoryReset(){
113   clearUserDataCache();
114 }
115 
clearUserDataCache()116 void libada::clearUserDataCache() {
117   cache_TOTP_name.clear();
118   cache_HOTP_name.clear();
119   cache_PWS_name.clear();
120   status_PWS.clear();
121 }
122 
123 
on_PWS_save(int slot_no)124 void libada::on_PWS_save(int slot_no){
125   cache_PWS_name.remove(slot_no);
126   status_PWS.clear();
127 }
128 
on_OTP_save(int slot_no,bool isHOTP)129 void libada::on_OTP_save(int slot_no, bool isHOTP){
130   if(isHOTP){
131     cache_HOTP_name.remove(slot_no);
132   } else {
133     cache_TOTP_name.remove(slot_no);
134   }
135   emit regenerateMenu();
136 }
137 
138 #include <QDebug>
getTOTPSlotName(const int i)139 std::string libada::getTOTPSlotName(const int i) {
140   return cache_TOTP_name.getName(i);
141 }
142 
have_communication_issues_occurred()143 bool libada::have_communication_issues_occurred(){
144   if(DeviceCommunicationException::has_occurred()){
145     DeviceCommunicationException::reset_occurred_flag();
146     return true;
147   }
148   return false;
149 }
150 
get_status_no_except()151 int libada::get_status_no_except(){
152   try{
153     nm::instance()->get_status();
154     return 0;
155   }
156   catch (DeviceCommunicationException &e){
157     return 1;
158   }
159   catch (LongOperationInProgressException &e){
160     return 2;
161   }
162   catch (const CommandFailedException &e){
163     return 3;
164   }
165   catch (const std::exception &e){
166     return 4;
167   }
168 }
169 
get_status()170 nitrokey::proto::stick10::GetStatus::ResponsePayload libada::get_status(){
171   try{
172     return nm::instance()->get_status();
173   }
174   catch (DeviceCommunicationException &e){
175     return nitrokey::proto::stick10::GetStatus::ResponsePayload();
176   }
177 }
178 
get_serial_number()179 std::string libada::get_serial_number(){
180   if(cardSerial_cached.empty())
181     try{
182       cardSerial_cached = nm::instance()->get_serial_number();
183     }
184   catch (DeviceCommunicationException &e){
185 
186   }
187   return cardSerial_cached;
188 }
189 
getHOTPSlotName(const int i)190 std::string libada::getHOTPSlotName(const int i) {
191   return cache_HOTP_name.getName(i);
192 }
193 
getPWSSlotName(const int i)194 std::string libada::getPWSSlotName(const int i) {
195   return cache_PWS_name.getName(i);
196 }
197 
getPWSSlotStatus(const int i)198 bool libada::getPWSSlotStatus(const int i) {
199   try{
200     if (status_PWS.empty()){
201       status_PWS = nm::instance()->get_password_safe_slot_status();
202     }
203   }
204   catch (DeviceCommunicationException &e){
205     return false;
206   }
207   return status_PWS[i];
208 }
209 
erasePWSSlot(const int i)210 void libada::erasePWSSlot(const int i) {
211   nm::instance()->erase_password_safe_slot(i);
212 }
213 
getStorageSDCardSizeGB()214 uint8_t libada::getStorageSDCardSizeGB() {
215   return nm::instance()->get_SD_card_size();
216 }
217 
isDeviceConnected() const218 bool libada::isDeviceConnected() const throw() {
219   auto conn = nm::instance()->is_connected();
220   return conn;
221 }
222 
isDeviceInitialized()223 bool libada::isDeviceInitialized() {
224   return true;
225 }
226 
isStorageDeviceConnected() const227 bool libada::isStorageDeviceConnected() const throw() {
228   return nm::instance()->is_connected() &&
229           nm::instance()->get_connected_device_model() == nitrokey::DeviceModel::STORAGE;
230 }
231 
isPasswordSafeAvailable()232 bool libada::isPasswordSafeAvailable() {
233   return true;
234 }
235 
236 #include <QDebug>
237 #include <libnitrokey/DeviceCommunicationExceptions.h>
isPasswordSafeUnlocked()238 bool libada::isPasswordSafeUnlocked() {
239   try{
240     nm::instance()->get_password_safe_slot_status();
241     return true;
242   }
243   catch (const LongOperationInProgressException &e){
244    return false;
245   }
246   catch (CommandFailedException &e){
247     if (e.reason_not_authorized())
248       return false;
249     throw;
250   }
251   catch (DeviceCommunicationException &e){
252     return false;
253   }
254 }
255 
isTOTPSlotProgrammed(const int i)256 bool libada::isTOTPSlotProgrammed(const int i) {
257   return !getTOTPSlotName(i).empty();
258 }
259 
isHOTPSlotProgrammed(const int i)260 bool libada::isHOTPSlotProgrammed(const int i) {
261   return !getHOTPSlotName(i).empty();
262 }
263 
writeToOTPSlot(const OTPSlot & otpconf,const char * tempPassword)264 void libada::writeToOTPSlot(const OTPSlot &otpconf, const char * tempPassword) {
265   const auto byteArray = tempPassword;
266   switch(otpconf.type){
267     case OTPSlot::OTPType::HOTP: {
268       nm::instance()->write_HOTP_slot(otpconf.slotNumber, otpconf.slotName, otpconf.secret, otpconf.interval,
269         otpconf.config_st.useEightDigits, otpconf.config_st.useEnter, otpconf.config_st.useTokenID,
270       otpconf.tokenID, byteArray);
271     }
272       break;
273     case OTPSlot::OTPType::TOTP:
274       nm::instance()->write_TOTP_slot(otpconf.slotNumber, otpconf.slotName, otpconf.secret, otpconf.interval,
275                                 otpconf.config_st.useEightDigits, otpconf.config_st.useEnter, otpconf.config_st.useTokenID,
276                                 otpconf.tokenID, byteArray);
277       break;
278     case OTPSlot::UNKNOWN:
279       throw std::runtime_error("invalid OTP data");
280       break;
281   }
282 }
283 
is_nkpro_07_rtm1()284 bool libada::is_nkpro_07_rtm1() {
285   return nm::instance()->get_connected_device_model() == nitrokey::DeviceModel::PRO
286       && getMinorFirmwareVersion() == 7;
287 }
288 
is_secret320_supported()289 bool libada::is_secret320_supported() {
290   //reused atomic_int for this tri-bool
291   //TODO rewrite with enum and templated atomic<> for better clarity
292   constexpr auto true_value = 1;
293   constexpr auto false_value = 0;
294   if (secret320_supported_cached == invalid_value){
295     try{
296       const auto is_320_OTP_secret_supported = nm::instance()->is_320_OTP_secret_supported();
297       secret320_supported_cached = is_320_OTP_secret_supported ? true_value : false_value;
298     }
299     catch (DeviceCommunicationException &e){}
300   }
301   return secret320_supported_cached == true_value;
302 }
303 
getTOTPCode(int slot_number,const char * user_temporary_password)304 std::string libada::getTOTPCode(int slot_number, const char *user_temporary_password) {
305   return nm::instance()->get_TOTP_code(slot_number, user_temporary_password);
306 }
307 
getHOTPCode(int slot_number,const char * user_temporary_password)308 std::string libada::getHOTPCode(int slot_number, const char *user_temporary_password) {
309   return nm::instance()->get_HOTP_code(slot_number, user_temporary_password);
310 }
311 
eraseHOTPSlot(const int i,const char * string)312 int libada::eraseHOTPSlot(const int i, const char *string) {
313   return nm::instance()->erase_hotp_slot(i, string);
314 }
315 
eraseTOTPSlot(const int i,const char * string)316 int libada::eraseTOTPSlot(const int i, const char *string) {
317   return nm::instance()->erase_totp_slot(i, string);
318 }
319 
320 #include <QDateTime>
is_time_synchronized()321 bool libada::is_time_synchronized() {
322   auto time = QDateTime::currentDateTimeUtc().toTime_t();
323   try{
324     nm::instance()->set_time_soft(time);
325     return true;
326   }
327   catch (const LongOperationInProgressException &e){
328     return false;
329   }
330   catch( CommandFailedException &e){
331     if (!e.reason_timestamp_warning())
332       throw;
333     return false;
334   }
335   return false;
336 }
337 
set_current_time()338 bool libada::set_current_time() {
339   auto time = QDateTime::currentDateTimeUtc().toTime_t();
340   nm::instance()->set_time(time);
341   return true;
342 }
343 
on_DeviceDisconnect()344 void libada::on_DeviceDisconnect() {
345   //TODO imp perf compare serial numbers to not clear cache if it is the same device
346   clearUserDataCache();
347   cardSerial_cached.clear();
348   minor_firmware_version_cached = invalid_value;
349   secret320_supported_cached = invalid_value;
350 }
351 
352 
getName(const int i)353 std::string NameCache::getName(const int i) {
354   QMutexLocker locker(&mut);
355   if (cache.contains(i)){
356     return *cache[i];
357   }
358   try{
359     const auto slot_name = getter(i);
360     cache.insert(i, new std::string(slot_name));
361     free(reinterpret_cast<void*>(const_cast<char*>(slot_name)));
362   }
363   catch (LongOperationInProgressException &e){
364     cache.insert(i, new std::string(""));
365   }
366   catch (CommandFailedException &e){
367     if (!e.reason_slot_not_programmed())
368       throw;
369     cache.insert(i, new std::string(""));
370   }
371   catch (DeviceCommunicationException &e){
372     cache.insert(i, new std::string("--error--"));
373   }
374   return *cache[i];
375 }
376 
remove(const int slot_no)377 void NameCache::remove(const int slot_no) {
378   QMutexLocker locker(&mut);
379   cache.remove(slot_no);
380 }
381 
clear()382 void NameCache::clear() {
383   QMutexLocker locker(&mut);
384   cache.clear();
385 }
386 
NameCache(const std::function<const char * (int)> & getter)387 NameCache::NameCache(const std::function<const char *(int)> &getter) : getter(
388     getter) {}
389 
setGetter(const std::function<const char * (int)> & getter)390 void NameCache::setGetter(const std::function<const char *(int)> &getter) {
391   NameCache::getter = getter;
392 }
393 
~NameCache()394 NameCache::~NameCache() {
395   clear();
396 }
397 
398