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 "StorageActions.h"
23 #include "src/ui/pindialog.h"
24 #include "src/utils/bool_values.h"
25 #include <libnitrokey/NitrokeyManager.h>
26 
27 #ifdef Q_OS_LINUX
28 #include "systemutils.h"
29 #include <sys/mount.h> // for unmounting on linux
30 #endif                 // Q_OS_LINUX
31 
32 #if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
33 #include <unistd.h> //for sync syscall
34 #endif              // Q_OS_LINUX || Q_OS_MAC
35 #include <OwnSleep.h>
36 #include <src/ui/stick20lockfirmwaredialog.h>
37 #include <src/ui/stick20hiddenvolumedialog.h>
38 
39 #define LOCAL_PASSWORD_SIZE 40
40 #include <memory>
41 #include <src/core/ThreadWorker.h>
42 #include <libnitrokey/stick20_commands.h>
43 
44 
unmountEncryptedVolumes()45 void unmountEncryptedVolumes() {
46     return;
47 #if defined(Q_OS_LINUX)
48   std::string endev = systemutils::getEncryptedDevice();
49   if (endev.size() < 1)
50     return;
51   std::string mntdir = systemutils::getMntPoint(endev);
52   qDebug() << "Unmounting " << mntdir.c_str();
53   // TODO polling with MNT_EXPIRE? test which will suit better
54   // int err = umount2("/dev/nitrospace", MNT_DETACH);
55   int err = umount(mntdir.c_str());
56   if (err != 0) {
57     qDebug() << "Unmount error: " << strerror(errno);
58   }
59 #endif // Q_OS_LINUX
60 }
61 
local_sync()62 void local_sync() {
63   // TODO TEST unmount during/after big data transfer
64   fflush(NULL); // for windows, not necessarly needed or working
65 #if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
66   sync();
67 #endif // Q_OS_LINUX || Q_OS_MAC
68   // manual says sync blocks until it's done, but they
69   // are not guaranteeing will this save data integrity anyway,
70   // additional sleep might help
71   OwnSleep::sleep(1);
72   // unmount does sync on its own additionally (if successful)
73   unmountEncryptedVolumes();
74 }
75 
76 
startStick20EnableCryptedVolume()77 void StorageActions::startStick20EnableCryptedVolume() {
78   bool answer;
79 
80   if (TRUE == HiddenVolumeActive) {
81     answer = csApplet()->yesOrNoBox(tr("This activity locks your hidden volume. Do you want to "
82                                            "proceed?\nTo avoid data loss, please unmount the partitions before "
83                                            "proceeding."), false);
84     if (!answer)
85       return;
86   }
87 
88   PinDialog dialog(PinType::USER_PIN, nullptr);
89 
90   const auto user_wants_to_proceed = QDialog::Accepted == dialog.exec();
91   if (user_wants_to_proceed) {
92     startProgressFunc(tr("Enabling encrypted volume")); //FIXME use existing translation
93     const auto s = dialog.getPassword();
94 
95     new ThreadWorker(
96     [s]() -> Data { // FIXME make s shared_ptr to delete after use //or secure string
97       Data data;
98       data["error"] = 0;
99 
100       auto m = nitrokey::NitrokeyManager::instance();
101       auto l = libada::i();
102       try{
103         local_sync();
104         bool enable_storage_v045_workaround = l->getMinorFirmwareVersion() <= 45;
105         m->unlock_encrypted_volume(s.c_str());
106         if (enable_storage_v045_workaround)
107           OwnSleep::sleep(5); //workaround for https://github.com/Nitrokey/nitrokey-storage-firmware/issues/31
108 
109         data["success"] = true;
110       }
111       catch (CommandFailedException &e){
112         data["error"] = e.last_command_status;
113         if (e.reason_wrong_password()){
114           data["wrong_password"] = true;
115         }
116       }
117       catch (DeviceCommunicationException &e){
118         data["error"] = -1;
119         data["comm_error"] = true;
120       }
121 
122       return data;
123     },
124     [this](Data data){
125       if(data["success"].toBool()){
126         CryptedVolumeActive = true;
127         emit storageStatusChanged();
128         show_message_function(tr("Encrypted volume enabled"));
129       } else if (data["wrong_password"].toBool()){
130         csApplet()->warningBox(tr("Could not enable encrypted volume.") + " " //FIXME use existing translation
131                                + tr("Wrong password."));
132 
133       } else {
134         csApplet()->warningBox(tr("Could not enable encrypted volume.") + " "
135                                + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
136       }
137       end_progress_function();
138     }, this);
139 
140   }
141 }
142 
startStick20DisableCryptedVolume()143 void StorageActions::startStick20DisableCryptedVolume() {
144   if (TRUE == CryptedVolumeActive) {
145     const bool user_wants_to_proceed = csApplet()->yesOrNoBox(tr("This activity locks your encrypted volume. Do you want to "
146                                                 "proceed?\nTo avoid data loss, please unmount the partitions before "
147                                                 "proceeding."), false);
148     if (!user_wants_to_proceed)
149       return;
150 
151     startProgressFunc(tr("Disabling encrypted volume")); //FIXME use existing translation
152 
153     new ThreadWorker(
154     []() -> Data {
155       Data data;
156 
157       try{
158         local_sync();
159         auto m = nitrokey::NitrokeyManager::instance();
160         m->lock_encrypted_volume();
161         data["success"] = true;
162       }
163       catch (CommandFailedException &e){
164         data["error"] = e.last_command_status;
165       }
166       catch (DeviceCommunicationException &e){
167         data["error"] = -1;
168         data["comm_error"] = true;
169       }
170 
171       return data;
172     },
173     [this](Data data){
174       if(data["success"].toBool()) {
175         CryptedVolumeActive = false;
176         emit storageStatusChanged();
177         show_message_function(tr("Encrypted volume disabled"));
178         //  for v0.50 and below ask for reinsertion to complete the lock procedure
179         if (libada::i()->getMinorFirmwareVersion() <= 50){
180           csApplet()->messageBox(
181               tr("To complete the lock procedure, please remove and reconnect the Nitrokey."));
182         }
183       } else {
184         csApplet()->warningBox(tr("Could not lock encrypted volume.") + " "
185                                + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
186       }
187       end_progress_function();
188     }, this);
189   }
190 }
191 
startStick20EnableHiddenVolume()192 void StorageActions::startStick20EnableHiddenVolume() {
193   if (!CryptedVolumeActive) {
194     csApplet()->warningBox(tr("Please enable the encrypted volume first."));
195     return;
196   }
197 
198   const bool user_wants_to_proceed =
199       csApplet()->yesOrNoBox(tr("This activity locks your encrypted volume. Do you want to "
200                                     "proceed?\nTo avoid data loss, please unmount the partitions before "
201                                     "proceeding."), true);
202   if (!user_wants_to_proceed)
203     return;
204 
205   PinDialog dialog(PinType::HIDDEN_VOLUME, nullptr);
206   const auto user_gives_password = dialog.exec() == QDialog::Accepted ;
207 
208   if (!user_gives_password) {
209     return;
210   }
211 
212   startProgressFunc(tr("Enabling hidden volume")); //FIXME use existing translation
213 
214   auto s = dialog.getPassword();
215 
216   new ThreadWorker(
217   [s]() -> Data { //FIXME transport throuugh shared_ptr or secure string
218     Data data;
219 
220     auto m = nitrokey::NitrokeyManager::instance();
221     auto l = libada::i();
222     try {
223       local_sync();
224       bool enable_storage_v045_workaround = l->getMinorFirmwareVersion() <= 45;
225       m->unlock_hidden_volume(s.c_str());
226 
227       if (enable_storage_v045_workaround)
228         OwnSleep::sleep(5); //workaround for https://github.com/Nitrokey/nitrokey-storage-firmware/issues/31
229 
230       data["success"] = true;
231     }
232     catch (CommandFailedException &e){
233       data["error"] = e.last_command_status;
234       if (e.reason_wrong_password()){
235         data["wrong_password"] = true;
236       }
237     }
238     catch (DeviceCommunicationException &e){
239       data["error"] = -1;
240       data["comm_error"] = true;
241     }
242 
243     return data;
244   },
245   [this](Data data){
246 
247     if(data["success"].toBool()){
248       HiddenVolumeActive = true;
249       emit storageStatusChanged();
250       show_message_function(tr("Hidden volume enabled"));//FIXME use existing translation
251     } else if (data["wrong_password"].toBool()){
252       csApplet()->warningBox(tr("Could not enable hidden volume.") + " " //FIXME use existing translation
253                              + tr("Wrong password."));
254 
255     } else {
256       csApplet()->warningBox(tr("Could not enable hidden volume.") + " "
257                              + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
258     }
259     end_progress_function();
260   }, this);
261 
262 }
263 
startStick20DisableHiddenVolume()264 void StorageActions::startStick20DisableHiddenVolume() {
265   const bool user_wants_to_proceed =
266       csApplet()->yesOrNoBox(tr("This activity locks your hidden volume. Do you want to proceed?\nTo "
267                                     "avoid data loss, please unmount the partitions before proceeding."), true);
268   if (!user_wants_to_proceed)
269     return;
270 
271   startProgressFunc(tr("Disabling hidden volume")); //FIXME use existing translation
272 
273 
274   new ThreadWorker(
275       []() -> Data {
276         Data data;
277 
278         try{
279           local_sync();
280           auto m = nitrokey::NitrokeyManager::instance();
281           m->lock_hidden_volume();
282           data["success"] = true;
283         }
284         catch (CommandFailedException &e){
285           data["error"] = e.last_command_status;
286         }
287         catch (DeviceCommunicationException &e){
288           data["error"] = -1;
289           data["comm_error"] = true;
290         }
291 
292         return data;
293       },
294       [this](Data data){
295         if(data["success"].toBool()) {
296           HiddenVolumeActive = false;
297           emit storageStatusChanged();
298           show_message_function(tr("Hidden volume disabled")); //FIXME use existing translation
299         }
300         else {
301           csApplet()->warningBox(tr("Could not lock hidden volume.") + " "
302                                  + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
303         }
304         end_progress_function();
305       }, this);
306 
307 }
308 
startLockDeviceAction(bool ask_for_confirmation)309 void StorageActions::startLockDeviceAction(bool ask_for_confirmation) {
310   bool user_wants_to_proceed;
311 
312   if ((ask_for_confirmation) && ((TRUE == CryptedVolumeActive) || (TRUE == HiddenVolumeActive))) {
313     user_wants_to_proceed = csApplet()->yesOrNoBox(tr("This activity locks your encrypted volume. Do you want to "
314                                            "proceed?\nTo avoid data loss, please unmount the partitions before "
315                                            "proceeding."), true);
316     if (!user_wants_to_proceed) {
317       return;
318     }
319   }
320 
321   startProgressFunc(tr("Locking device")); //FIXME use existing translation
322 
323 
324   new ThreadWorker(
325     []() -> Data {
326       Data data;
327       try {
328         local_sync();
329         auto m = nitrokey::NitrokeyManager::instance();
330         m->lock_device();
331         data["success"] = true;
332       }
333       catch (CommandFailedException &e){
334         data["error"] = e.last_command_status;
335       }
336       catch (DeviceCommunicationException &e){
337         data["error"] = -1;
338         data["comm_error"] = true;
339       }
340       return data;
341     },
342     [this](Data data){
343       if(data["success"].toBool()) {
344         HiddenVolumeActive = false;
345         CryptedVolumeActive = false;
346         emit storageStatusChanged();
347         show_message_function(tr("Device locked")); //FIXME use existing translation
348         //  for v0.50 and below ask for reinsertion to complete the lock procedure
349         if (libada::i()->getMinorFirmwareVersion() <= 50){
350           csApplet()->messageBox(
351               tr("To complete the lock procedure, please remove and reconnect the Nitrokey."));
352         }
353       }
354       else {
355         csApplet()->warningBox(tr("Could not lock device.") + " "
356                                + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
357       }
358 
359       end_progress_function();
360     }, this);
361 
362 }
363 
364 #include "stick20updatedialog.h"
365 
startStick20EnableFirmwareUpdate()366 void StorageActions::startStick20EnableFirmwareUpdate() {
367   UpdateDialog dialogUpdate(nullptr);
368 
369   bool user_wants_to_proceed = dialogUpdate.exec() == QDialog::Accepted;
370   if (!user_wants_to_proceed) {
371     return;
372   }
373 
374   auto successMessage = tr("Device set in update mode");
375   auto failureMessage = tr("Device could not be set in update mode.");
376 
377   bool success = false;
378   // try with default password
379   // ask for password when fails
380   runAndHandleErrorsInUI(successMessage, "",
381                          [&success](){ //FIXME use secure string
382                            local_sync();
383                            auto m = nitrokey::NitrokeyManager::instance();
384                            m->enable_firmware_update("12345678");
385                            success = true;
386                          },[](){});
387 
388   if(success) return;
389 
390   PinDialog dialog(PinType::FIRMWARE_PIN);
391   user_wants_to_proceed = QDialog::Accepted == dialog.exec();
392 
393   if (!user_wants_to_proceed) {
394     return;
395   }
396   auto s = dialog.getPassword();
397 
398   runAndHandleErrorsInUI(successMessage, failureMessage,
399      [s](){ //FIXME use secure string
400          local_sync();
401          auto m = nitrokey::NitrokeyManager::instance();
402          m->enable_firmware_update(s.c_str());
403        },[](){});
404 }
405 
406 
startStick20ExportFirmwareToFile()407 void StorageActions::startStick20ExportFirmwareToFile() {
408   PinDialog dialog(PinType::ADMIN_PIN);
409   bool user_provided_PIN = QDialog::Accepted == dialog.exec();
410   if (!user_provided_PIN) {
411     return;
412   }
413   auto s = dialog.getPassword(); //FIXME use secure string
414 
415   //FIXME use existing translation
416   runAndHandleErrorsInUI(tr("Firmware exported"), tr("Could not export firmware."), [s](){
417     auto m = nitrokey::NitrokeyManager::instance();
418     m->export_firmware(s.c_str());
419   }, [](){});
420 }
421 
startStick20DestroyCryptedVolume(int fillSDWithRandomChars)422 void StorageActions::startStick20DestroyCryptedVolume(int fillSDWithRandomChars) {
423   bool user_entered_PIN;
424   bool answer;
425 
426   answer = (fillSDWithRandomChars == 1) || csApplet()->yesOrNoBox(tr("WARNING: Generating new AES keys will destroy the encrypted volumes, "
427                                          "hidden volumes, and password safe! Continue?"), false);
428   if (answer) {
429     PinDialog dialog(PinType::ADMIN_PIN);
430     user_entered_PIN = QDialog::Accepted == dialog.exec();
431     if (!user_entered_PIN) {
432       return;
433     }
434     auto s = dialog.getPassword();
435 
436     startProgressFunc(tr("Generating new AES keys")); //FIXME use existing translation
437 
438     new ThreadWorker(
439     [s]() -> Data { //FIXME use secure string
440       Data data;
441       try{
442         auto m = nitrokey::NitrokeyManager::instance();
443         m->lock_device(); //lock device to reset its state
444         m->build_aes_key(s.c_str());
445         data["success"] = true;
446       }
447       catch (CommandFailedException &e){
448         data["error"] = e.last_command_status;
449         if (e.reason_wrong_password()){
450           data["wrong_password"] = true;
451         }
452       }
453       catch (DeviceCommunicationException &e){
454         data["error"] = -1;
455         data["comm_error"] = true;
456       }
457       return data;
458     },
459     [this, fillSDWithRandomChars, s](Data data){ // FIXME use secure string
460       if(data["success"].toBool()) {
461         emit FactoryReset();
462         show_message_function(tr("New AES keys generated")); //FIXME use existing translation
463         if (fillSDWithRandomChars != 0) {
464           _execute_SD_clearing(s);
465         }
466       } else if (data["wrong_password"].toBool()){
467         csApplet()->warningBox(tr("Keys could not be generated.") + " " //FIXME use existing translation
468                                + tr("Wrong password."));
469 
470       } else {
471         csApplet()->warningBox(tr("Keys could not be generated.") + " "
472                                + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
473       }
474       end_progress_function();
475     }, this);
476   }
477 }
478 
_execute_SD_clearing(const std::string & s)479 void StorageActions::_execute_SD_clearing(const std::string &s) {
480 //does not need long operation indicator
481   new ThreadWorker(
482     [s]() -> Data { //FIXME use secure string
483       Data data;
484       data["success"] = false;
485       try{
486         auto m = nitrokey::NitrokeyManager::instance();
487         m->fill_SD_card_with_random_data(s.c_str());
488       }
489       catch (LongOperationInProgressException &l){
490         //expected
491         data["success"] = true;
492       }
493       catch (CommandFailedException &e){
494         data["error"] = e.last_command_status;
495         if (e.reason_wrong_password()){
496           data["wrong_password"] = true;
497         }
498       }
499       catch (DeviceCommunicationException &e){
500         data["error"] = -1;
501         data["comm_error"] = true;
502       }
503 
504       return data;
505     },
506     [this](Data data){
507       if(data["success"].toBool()) {
508         QTimer::singleShot(1000, [this](){
509           emit storageStatusUpdated();
510           emit longOperationStarted();
511           emit storageStatusChanged();
512         });
513       } else if (data["wrong_password"].toBool()){
514         csApplet()->warningBox(tr("Could not clear SD card.") + " " //FIXME use existing translation
515                                + tr("Wrong password."));
516 
517       } else {
518         csApplet()->warningBox(tr("Could not clear SD card.") + " "
519                                + tr("Status code: %1").arg(data["error"].toInt())); //FIXME use existing translation
520       }
521     }, this);
522 }
523 
startStick20FillSDCardWithRandomChars()524 void StorageActions::startStick20FillSDCardWithRandomChars() {
525   PinDialog dialog(PinType::ADMIN_PIN);
526   bool user_provided_PIN = QDialog::Accepted == dialog.exec();
527   if (user_provided_PIN) {
528     auto s = dialog.getPassword();
529     _execute_SD_clearing(s);
530   }
531 }
532 
runAndHandleErrorsInUI(QString successMessage,QString operationFailureMessage,std::function<void (void)> codeToRunInDeviceThread,std::function<void (void)> onSuccessInGuiThread)533 void StorageActions::runAndHandleErrorsInUI(QString successMessage, QString operationFailureMessage,
534                                             std::function<void(void)> codeToRunInDeviceThread,
535                                             std::function<void(void)> onSuccessInGuiThread) {
536 //  startProgressFunc(successMessage); //TODO enable after moving blocking code to separate thread
537   try{
538     //TODO run in separate thread
539     codeToRunInDeviceThread();
540     onSuccessInGuiThread();
541     show_message_function(successMessage);
542   }
543   catch (CommandFailedException &e){
544     if (!operationFailureMessage.isEmpty()){
545       if (e.reason_wrong_password()){
546         csApplet()->warningBox(operationFailureMessage + " "
547                                + tr("Wrong password."));
548       } else {
549         csApplet()->warningBox(operationFailureMessage + " "
550                                + tr("Status code: %1").arg(e.last_command_status));
551       }
552     }
553   }
554   catch (DeviceCommunicationException &e){
555     if (!operationFailureMessage.isEmpty()){
556       csApplet()->warningBox(operationFailureMessage + " " +
557                            tr("Communication issue."));
558     }
559   }
560 //  end_progress_function();
561 }
562 
563 
startStick20ClearNewSdCardFound()564 void StorageActions::startStick20ClearNewSdCardFound() {
565   PinDialog dialog(PinType::ADMIN_PIN);
566 
567   bool user_provided_pin = QDialog::Accepted == dialog.exec();
568   if (!user_provided_pin) {
569     return;
570   }
571 
572   auto s = dialog.getPassword();
573   auto operationFailureMessage = tr("Flag cannot be cleared."); //FIXME use existing translation
574   auto operationSuccessMessage = tr("Flag cleared.");
575 
576   runAndHandleErrorsInUI(QString(), operationFailureMessage, [s]() { //FIXME use secure string
577     auto m = nitrokey::NitrokeyManager::instance();
578     m->clear_new_sd_card_warning(s.c_str());
579   }, [this]() {
580     emit storageStatusUpdated();
581   });
582 }
583 
584 
startStick20SetReadOnlyUncryptedVolume()585 void StorageActions::startStick20SetReadOnlyUncryptedVolume() {
586   using nm = nitrokey::NitrokeyManager;
587   auto type = PinType::ADMIN_PIN;
588   if(nm::instance()->set_unencrypted_volume_rorw_pin_type_user()){
589     type = PinType::USER_PIN;
590   }
591   PinDialog dialog(type);
592 
593   bool user_provided_pin = QDialog::Accepted == dialog.exec();
594   if (!user_provided_pin) {
595     return;
596   }
597 
598   auto operationFailureMessage = tr("Cannot set unencrypted volume read-only"); //FIXME use existing translation
599   auto operationSuccessMessage = tr("Unencrypted volume set read-only"); //FIXME use existing translation
600   auto s = dialog.getPassword();
601 
602   runAndHandleErrorsInUI(operationSuccessMessage, operationFailureMessage, [s, type]() {
603     auto m = nitrokey::NitrokeyManager::instance();
604     //FIXME use secure string
605     switch(type){
606       case USER_PIN:
607         m->set_unencrypted_read_only(s.c_str());
608         break;
609       case ADMIN_PIN:
610         m->set_unencrypted_read_only_admin(s.c_str());
611         break;
612       default:
613         break;
614     }
615   }, [this]() {
616     emit storageStatusChanged();
617   });
618 
619 }
620 
startStick20SetReadWriteUncryptedVolume()621 void StorageActions::startStick20SetReadWriteUncryptedVolume() {
622   using nm = nitrokey::NitrokeyManager;
623   auto type = PinType::ADMIN_PIN;
624   if(nm::instance()->set_unencrypted_volume_rorw_pin_type_user()){
625     type = PinType::USER_PIN;
626   }
627   PinDialog dialog(type);
628 
629   bool user_provided_pin = QDialog::Accepted == dialog.exec();
630   if (!user_provided_pin) {
631     return;
632   }
633 
634   auto operationFailureMessage = tr("Cannot set unencrypted volume read-write"); //FIXME use existing translation
635   auto operationSuccessMessage = tr("Unencrypted volume set read-write"); //FIXME use existing translation
636   auto s = dialog.getPassword();
637 
638   runAndHandleErrorsInUI(operationSuccessMessage, operationFailureMessage, [s, type]() {
639     auto m = nitrokey::NitrokeyManager::instance();
640     //FIXME use secure string
641     switch(type){
642       case USER_PIN:
643         m->set_unencrypted_read_write(s.c_str());
644         break;
645       case ADMIN_PIN:
646         m->set_unencrypted_read_write_admin(s.c_str());
647         break;
648       default:
649         break;
650     }
651   }, [this]() {
652     emit storageStatusChanged();
653   });
654 
655 }
656 
startStick20SetReadOnlyEncryptedVolume()657 void StorageActions::startStick20SetReadOnlyEncryptedVolume() {
658   PinDialog dialog(PinType::ADMIN_PIN);
659 
660   bool user_provided_pin = QDialog::Accepted == dialog.exec();
661   if (!user_provided_pin) {
662     return;
663   }
664 
665   auto operationFailureMessage = tr("Cannot set encrypted volume read-only"); //FIXME use existing translation
666   auto operationSuccessMessage = tr("Encrypted volume set read-only"); //FIXME use existing translation
667   auto s = dialog.getPassword();
668 
669   runAndHandleErrorsInUI(operationSuccessMessage, operationFailureMessage, [s]() {
670     auto m = nitrokey::NitrokeyManager::instance();
671     m->set_encrypted_volume_read_only(s.c_str()); //FIXME use secure string
672   }, [this]() {
673     emit storageStatusChanged();
674   });
675 
676 }
677 
startStick20SetReadWriteEncryptedVolume()678 void StorageActions::startStick20SetReadWriteEncryptedVolume() {
679   PinDialog dialog(PinType::ADMIN_PIN);
680 
681   bool user_provided_pin = QDialog::Accepted == dialog.exec();
682   if (!user_provided_pin) {
683     return;
684   }
685 
686   auto operationFailureMessage = tr("Cannot set encrypted volume read-write"); //FIXME use existing translation
687   auto operationSuccessMessage = tr("Encrypted volume set read-write"); //FIXME use existing translation
688   auto s = dialog.getPassword();
689 
690   runAndHandleErrorsInUI(operationSuccessMessage, operationFailureMessage, [s]() {
691     auto m = nitrokey::NitrokeyManager::instance();
692     m->set_encrypted_volume_read_write(s.c_str()); //FIXME use secure string
693   }, [this]() {
694     emit storageStatusChanged();
695   });
696 
697 }
698 
startStick20LockStickHardware()699 void StorageActions::startStick20LockStickHardware() {
700   csApplet()->messageBox(tr("Functionality not implemented in current version")); //FIXME use existing translation
701   return;
702 
703   stick20LockFirmwareDialog firmwareDialog(nullptr);
704   bool user_wants_to_proceed = QDialog::Accepted == firmwareDialog.exec();
705   if (user_wants_to_proceed) {
706     PinDialog pinDialog(PinType::ADMIN_PIN);
707 
708     bool user_provided_PIN = pinDialog.exec() == QDialog::Accepted;
709     if (!user_provided_PIN) {
710       return;
711     }
712     const auto pass = pinDialog.getPassword();
713 // TODO     stick20SendCommand(STICK20_CMD_SEND_LOCK_STICK_HARDWARE, password);
714   }
715 }
716 
startStick20SetupHiddenVolume()717 void StorageActions::startStick20SetupHiddenVolume() {
718   stick20HiddenVolumeDialog HVDialog(nullptr);
719 
720   if (FALSE == CryptedVolumeActive) {
721     csApplet()->warningBox(tr("Please enable the encrypted volume first."));
722     return;
723   }
724 
725   auto operationSuccessMessage = tr("Hidden volume created");
726   auto operationFailureMessage = tr("Hidden volume could not be created."); //FIXME use existing translation
727 
728   bool user_wants_to_proceed = HVDialog.exec() == QDialog::Accepted;
729   if (!user_wants_to_proceed) {
730     return;
731   }
732   const auto d = HVDialog.HV_Setup_st; //FIXME clear securely struct
733   auto p = std::string( reinterpret_cast< char const* >(d.HiddenVolumePassword_au8));
734 
735   runAndHandleErrorsInUI(operationSuccessMessage, operationFailureMessage, [d, p](){ //FIXME use secure string
736     auto m = nitrokey::NitrokeyManager::instance();
737     m->create_hidden_volume(d.SlotNr_u8, d.StartBlockPercent_u8,
738                             d.EndBlockPercent_u8, p.c_str());
739   }, [](){});
740 
741 }
742 
StorageActions(QObject * parent,Authentication * auth_admin,Authentication * auth_user)743 StorageActions::StorageActions(QObject *parent, Authentication *auth_admin, Authentication *auth_user) : QObject(
744     parent), auth_admin(auth_admin), auth_user(auth_user) {
745   connect(this, SIGNAL(storageStatusChanged()), this, SLOT(on_StorageStatusChanged()));
746 }
747 
748 #include <QDebug>
749 
750 
on_StorageStatusChanged()751 void StorageActions::on_StorageStatusChanged() {
752   if (!libada::i()->isStorageDeviceConnected())
753     return;
754 
755   new ThreadWorker(
756     []() -> Data {
757         bool interrupt = QThread::currentThread()->isInterruptionRequested();
758         static bool first_run = true;
759         int times = first_run? 5 : 3;
760         first_run = false;
761 
762         for (int i = 0; i < times && !interrupt; ++i) {
763             QThread::currentThread()->msleep(500);
764             interrupt = QThread::currentThread()->isInterruptionRequested();
765         }
766       Data data;
767         if(interrupt) {
768             return data;
769         }
770 
771         auto m = nitrokey::NitrokeyManager::instance();
772         auto s = m->get_status_storage();
773         data["encrypted_active"] = s.VolumeActiceFlag_st.encrypted;
774         data["hidden_active"] = s.VolumeActiceFlag_st.hidden;
775         return data;
776     },
777     [this](Data data){
778         CryptedVolumeActive = data["encrypted_active"].toBool();
779         HiddenVolumeActive = data["hidden_active"].toBool();
780         emit storageStatusUpdated();
781     }, this, "update storage status");
782 }
783 
set_start_progress_window(std::function<void (QString)> _start_progress_function)784 void StorageActions::set_start_progress_window(std::function<void(QString)> _start_progress_function) {
785   startProgressFunc = _start_progress_function;
786 }
787 
set_end_progress_window(std::function<void ()> _end_progress_function)788 void StorageActions::set_end_progress_window(std::function<void()> _end_progress_function) {
789   end_progress_function = _end_progress_function;
790 }
791 
set_show_message(std::function<void (QString)> _show_message)792 void StorageActions::set_show_message(std::function<void(QString)> _show_message) {
793   show_message_function = _show_message;
794 }
795