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