summaryrefslogtreecommitdiff
path: root/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2019-08-12 22:23:33 -0700
committerDaniel Mueller <deso@posteo.net>2019-08-12 22:23:33 -0700
commit47ea358ddc1bc37b809f9f5b893a6b099cbb262b (patch)
tree322f204230693883c1d9759cb2f88aaddc0e3b7a /nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc
parent4dc73375e0364aea70b52682b916635b7b75a2eb (diff)
downloadnitrocli-47ea358ddc1bc37b809f9f5b893a6b099cbb262b.tar.gz
nitrocli-47ea358ddc1bc37b809f9f5b893a6b099cbb262b.tar.bz2
Update nitrokey crate to 0.4.0-alpha.3
This change updates the version of the nitrokey crate that we use to 0.4.0-alpha.3. This version is the supposedly last pre-release before 0.4.0, with no further major anticipated changes. In order to integrate with this new version we have to adjust the way we connect to a Nitrokey device by funneling those connection requests through a global manager object. The rationale behind that step being that the underlying libnitrokey actually cannot handle access of multiple devices at the same time, and so the manager object is used to prevent accidental wrong concurrent usage. Because a device object now effectively keeps a reference to the manager, we need to provide an additional lifetime to that and derived objects. Lastly, the use of a manager is also the reason why the tests had to be adjusted to no longer accept device objects in their signatures, but only the respective model for which to invoke the test. That is required because, as elaborated earlier on, having a device object implies having taken a reference to a manager (in that case owned by nitrokey-test), and that reference clashes with the nitrocli code itself attempting to take the manager. We side step this problem by merely accepting a Model object, which can be passed around independently of the manager itself, meaning that nitrokey-test does not need to hold such a reference while the test is run. Import subrepo nitrokey/:nitrokey at f150d59410eefdec2ae69b2422906a3d1d88aa07 Import subrepo nitrokey-sys/:nitrokey-sys at 8695e2c762807e033a86c8d03974b686d20cdd72 Import subrepo lazy-static/:lazy-static at b4b2b16aaa79dd7548e288455a0dbe4065bf4e1a
Diffstat (limited to 'nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc')
-rw-r--r--nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc1199
1 files changed, 1199 insertions, 0 deletions
diff --git a/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc b/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc
new file mode 100644
index 0000000..6c26a43
--- /dev/null
+++ b/nitrokey-sys/libnitrokey-v3.5/NitrokeyManager.cc
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (c) 2015-2018 Nitrokey UG
+ *
+ * This file is part of libnitrokey.
+ *
+ * libnitrokey is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * libnitrokey is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0
+ */
+
+#include <cstring>
+#include <iostream>
+#include "libnitrokey/NitrokeyManager.h"
+#include "libnitrokey/LibraryException.h"
+#include <algorithm>
+#include <unordered_map>
+#include <stick20_commands.h>
+#include "libnitrokey/misc.h"
+#include <mutex>
+#include "libnitrokey/cxx_semantics.h"
+#include "libnitrokey/misc.h"
+#include <functional>
+#include <stick10_commands.h>
+
+std::mutex nitrokey::proto::send_receive_mtx;
+
+namespace nitrokey{
+
+ std::mutex mex_dev_com_manager;
+
+#ifndef strndup
+#ifdef _WIN32
+#pragma message "Using own strndup"
+char * strndup(const char* str, size_t maxlen){
+ size_t len = strnlen(str, maxlen);
+ char* dup = (char *) malloc(len + 1);
+ memcpy(dup, str, len);
+ dup[len] = 0;
+ return dup;
+}
+#endif
+#endif
+
+using nitrokey::misc::strcpyT;
+
+ template <typename T>
+ typename T::CommandPayload get_payload(){
+ //Create, initialize and return by value command payload
+ typename T::CommandPayload st;
+ bzero(&st, sizeof(st));
+ return st;
+ }
+
+
+ // package type to auth, auth type [Authorize,UserAuthorize]
+ template <typename S, typename A, typename T>
+ void NitrokeyManager::authorize_packet(T &package, const char *admin_temporary_password, shared_ptr<Device> device){
+ if (!is_authorization_command_supported()){
+ LOG("Authorization command not supported, skipping", Loglevel::WARNING);
+ }
+ auto auth = get_payload<A>();
+ strcpyT(auth.temporary_password, admin_temporary_password);
+ auth.crc_to_authorize = S::CommandTransaction::getCRC(package);
+ A::CommandTransaction::run(device, auth);
+ }
+
+ shared_ptr <NitrokeyManager> NitrokeyManager::_instance = nullptr;
+
+ NitrokeyManager::NitrokeyManager() : device(nullptr)
+ {
+ set_debug(false);
+ }
+ NitrokeyManager::~NitrokeyManager() {
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+
+ for (auto d : connected_devices){
+ if (d.second == nullptr) continue;
+ d.second->disconnect();
+ connected_devices[d.first] = nullptr;
+ }
+ }
+
+ bool NitrokeyManager::set_current_device_speed(int retry_delay, int send_receive_delay){
+ if (retry_delay < 20 || send_receive_delay < 20){
+ LOG("Delay set too low: " + to_string(retry_delay) +" "+ to_string(send_receive_delay), Loglevel::WARNING);
+ return false;
+ }
+
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ if(device == nullptr) {
+ return false;
+ }
+ device->set_receiving_delay(std::chrono::duration<int, std::milli>(send_receive_delay));
+ device->set_retry_delay(std::chrono::duration<int, std::milli>(retry_delay));
+ return true;
+ }
+
+ std::vector<DeviceInfo> NitrokeyManager::list_devices(){
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+
+ return Device::enumerate();
+ }
+
+ std::vector<std::string> NitrokeyManager::list_devices_by_cpuID(){
+ using misc::toHex;
+ //disconnect default device
+ disconnect();
+
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ LOGD1("Disconnecting registered devices");
+ for (auto & kv : connected_devices_byID){
+ if (kv.second != nullptr)
+ kv.second->disconnect();
+ }
+ connected_devices_byID.clear();
+
+ LOGD1("Enumerating devices");
+ std::vector<std::string> res;
+ const auto v = Device::enumerate();
+ LOGD1("Discovering IDs");
+ for (auto & i: v){
+ if (i.m_deviceModel != DeviceModel::STORAGE)
+ continue;
+ auto p = i.m_path;
+ auto d = make_shared<Stick20>();
+ LOGD1( std::string("Found: ") + p );
+ d->set_path(p);
+ try{
+ if (d->connect()){
+ device = d;
+ std::string id;
+ try {
+ const auto status = get_status_storage();
+ const auto sc_id = toHex(status.ActiveSmartCardID_u32);
+ const auto sd_id = toHex(status.ActiveSD_CardID_u32);
+ id += sc_id + ":" + sd_id;
+ id += "_p_" + p;
+ }
+ catch (const LongOperationInProgressException &e) {
+ LOGD1(std::string("Long operation in progress, setting ID to: ") + p);
+ id = p;
+ }
+
+ connected_devices_byID[id] = d;
+ res.push_back(id);
+ LOGD1( std::string("Found: ") + p + " => " + id);
+ } else{
+ LOGD1( std::string("Could not connect to: ") + p);
+ }
+ }
+ catch (const DeviceCommunicationException &e){
+ LOGD1( std::string("Exception encountered: ") + p);
+ }
+ }
+ return res;
+ }
+
+ bool NitrokeyManager::connect_with_ID(const std::string id) {
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+
+ auto position = connected_devices_byID.find(id);
+ if (position == connected_devices_byID.end()) {
+ LOGD1(std::string("Could not find device ")+id + ". Refresh devices list with list_devices_by_cpuID().");
+ return false;
+ }
+
+ auto d = connected_devices_byID[id];
+ device = d;
+ current_device_id = id;
+
+ //validate connection
+ try{
+ get_status();
+ }
+ catch (const LongOperationInProgressException &){
+ //ignore
+ }
+ catch (const DeviceCommunicationException &){
+ d->disconnect();
+ current_device_id = "";
+ connected_devices_byID[id] = nullptr;
+ connected_devices_byID.erase(position);
+ return false;
+ }
+ nitrokey::log::Log::setPrefix(id);
+ LOGD1("Device successfully changed");
+ return true;
+ }
+
+ /**
+ * Connects device to path.
+ * Assumes devices are not being disconnected and caches connections (param cache_connections).
+ * @param path os-dependent device path
+ * @return false, when could not connect, true otherwise
+ */
+ bool NitrokeyManager::connect_with_path(std::string path) {
+ const bool cache_connections = false;
+
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+
+ if (cache_connections){
+ if(connected_devices.find(path) != connected_devices.end()
+ && connected_devices[path] != nullptr) {
+ device = connected_devices[path];
+ return true;
+ }
+ }
+
+ auto info_ptr = hid_enumerate(NITROKEY_VID, 0);
+ auto first_info_ptr = info_ptr;
+ if (!info_ptr)
+ return false;
+
+ misc::Option<DeviceModel> model;
+ while (info_ptr && !model.has_value()) {
+ if (path == std::string(info_ptr->path)) {
+ model = product_id_to_model(info_ptr->product_id);
+ }
+ info_ptr = info_ptr->next;
+ }
+ hid_free_enumeration(first_info_ptr);
+
+ if (!model.has_value())
+ return false;
+
+ auto p = Device::create(model.value());
+ if (!p)
+ return false;
+ p->set_path(path);
+
+ if(!p->connect()) return false;
+
+ if(cache_connections){
+ connected_devices [path] = p;
+ }
+
+ device = p; //previous device will be disconnected automatically
+ current_device_id = path;
+ nitrokey::log::Log::setPrefix(path);
+ LOGD1("Device successfully changed");
+ return true;
+ }
+
+ bool NitrokeyManager::connect() {
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ vector< shared_ptr<Device> > devices = { make_shared<Stick10>(), make_shared<Stick20>() };
+ bool connected = false;
+ for( auto & d : devices ){
+ if (d->connect()){
+ device = std::shared_ptr<Device>(d);
+ connected = true;
+ }
+ }
+ return connected;
+ }
+
+
+ void NitrokeyManager::set_log_function(std::function<void(std::string)> log_function){
+ static nitrokey::log::FunctionalLogHandler handler(log_function);
+ nitrokey::log::Log::instance().set_handler(&handler);
+ }
+
+ bool NitrokeyManager::set_default_commands_delay(int delay){
+ if (delay < 20){
+ LOG("Delay set too low: " + to_string(delay), Loglevel::WARNING);
+ return false;
+ }
+ Device::set_default_device_speed(delay);
+ return true;
+ }
+
+ bool NitrokeyManager::connect(const char *device_model) {
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ LOG(__FUNCTION__, nitrokey::log::Loglevel::DEBUG_L2);
+ switch (device_model[0]){
+ case 'P':
+ device = make_shared<Stick10>();
+ break;
+ case 'S':
+ device = make_shared<Stick20>();
+ break;
+ default:
+ throw std::runtime_error("Unknown model");
+ }
+ return device->connect();
+ }
+
+ bool NitrokeyManager::connect(device::DeviceModel device_model) {
+ const char *model_string;
+ switch (device_model) {
+ case device::DeviceModel::PRO:
+ model_string = "P";
+ break;
+ case device::DeviceModel::STORAGE:
+ model_string = "S";
+ break;
+ default:
+ throw std::runtime_error("Unknown model");
+ }
+ return connect(model_string);
+ }
+
+ shared_ptr<NitrokeyManager> NitrokeyManager::instance() {
+ static std::mutex mutex;
+ std::lock_guard<std::mutex> lock(mutex);
+ if (_instance == nullptr){
+ _instance = make_shared<NitrokeyManager>();
+ }
+ return _instance;
+ }
+
+
+
+ bool NitrokeyManager::disconnect() {
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ return _disconnect_no_lock();
+ }
+
+ bool NitrokeyManager::_disconnect_no_lock() {
+ //do not use directly without locked mutex,
+ //used by could_be_enumerated, disconnect
+ if (device == nullptr){
+ return false;
+ }
+ const auto res = device->disconnect();
+ device = nullptr;
+ return res;
+ }
+
+ bool NitrokeyManager::is_connected() throw(){
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ if(device != nullptr){
+ auto connected = device->could_be_enumerated();
+ if(connected){
+ return true;
+ } else {
+ _disconnect_no_lock();
+ return false;
+ }
+ }
+ return false;
+ }
+
+ bool NitrokeyManager::could_current_device_be_enumerated() {
+ std::lock_guard<std::mutex> lock(mex_dev_com_manager);
+ if (device != nullptr) {
+ return device->could_be_enumerated();
+ }
+ return false;
+ }
+
+ void NitrokeyManager::set_loglevel(int loglevel) {
+ loglevel = max(loglevel, static_cast<int>(Loglevel::ERROR));
+ loglevel = min(loglevel, static_cast<int>(Loglevel::DEBUG_L2));
+ Log::instance().set_loglevel(static_cast<Loglevel>(loglevel));
+ }
+
+ void NitrokeyManager::set_loglevel(Loglevel loglevel) {
+ Log::instance().set_loglevel(loglevel);
+ }
+
+ void NitrokeyManager::set_debug(bool state) {
+ if (state){
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+ } else {
+ Log::instance().set_loglevel(Loglevel::ERROR);
+ }
+ }
+
+
+ string NitrokeyManager::get_serial_number() {
+ if (device == nullptr) { return ""; };
+ switch (device->get_device_model()) {
+ case DeviceModel::PRO: {
+ auto response = GetStatus::CommandTransaction::run(device);
+ return nitrokey::misc::toHex(response.data().card_serial_u32);
+ }
+ break;
+
+ case DeviceModel::STORAGE:
+ {
+ auto response = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return nitrokey::misc::toHex(response.data().ActiveSmartCardID_u32);
+ }
+ break;
+ }
+ return "NA";
+ }
+
+ stick10::GetStatus::ResponsePayload NitrokeyManager::get_status(){
+ try{
+ auto response = GetStatus::CommandTransaction::run(device);
+ return response.data();
+ }
+ catch (DeviceSendingFailure &e){
+// disconnect();
+ throw;
+ }
+ }
+
+ string NitrokeyManager::get_status_as_string() {
+ auto response = GetStatus::CommandTransaction::run(device);
+ return response.data().dissect();
+ }
+
+ string getFilledOTPCode(uint32_t code, bool use_8_digits){
+ stringstream s;
+ s << std::right << std::setw(use_8_digits ? 8 : 6) << std::setfill('0') << code;
+ return s.str();
+ }
+
+ string NitrokeyManager::get_HOTP_code(uint8_t slot_number, const char *user_temporary_password) {
+ if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+
+ if (is_authorization_command_supported()){
+ auto gh = get_payload<GetHOTP>();
+ gh.slot_number = get_internal_slot_number_for_hotp(slot_number);
+ if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen
+ authorize_packet<GetHOTP, UserAuthorize>(gh, user_temporary_password, device);
+ }
+ auto resp = GetHOTP::CommandTransaction::run(device, gh);
+ return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
+ } else {
+ auto gh = get_payload<stick10_08::GetHOTP>();
+ gh.slot_number = get_internal_slot_number_for_hotp(slot_number);
+ if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0) {
+ strcpyT(gh.temporary_user_password, user_temporary_password);
+ }
+ auto resp = stick10_08::GetHOTP::CommandTransaction::run(device, gh);
+ return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
+ }
+ return "";
+ }
+
+ bool NitrokeyManager::is_internal_hotp_slot_number(uint8_t slot_number) const { return slot_number < 0x20; }
+ bool NitrokeyManager::is_valid_hotp_slot_number(uint8_t slot_number) const { return slot_number < 3; }
+ bool NitrokeyManager::is_valid_totp_slot_number(uint8_t slot_number) const { return slot_number < 0x10-1; } //15
+ uint8_t NitrokeyManager::get_internal_slot_number_for_totp(uint8_t slot_number) const { return (uint8_t) (0x20 + slot_number); }
+ uint8_t NitrokeyManager::get_internal_slot_number_for_hotp(uint8_t slot_number) const { return (uint8_t) (0x10 + slot_number); }
+
+
+
+ string NitrokeyManager::get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
+ uint8_t last_interval,
+ const char *user_temporary_password) {
+ if(!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ slot_number = get_internal_slot_number_for_totp(slot_number);
+
+ if (is_authorization_command_supported()){
+ auto gt = get_payload<GetTOTP>();
+ gt.slot_number = slot_number;
+ gt.challenge = challenge;
+ gt.last_interval = last_interval;
+ gt.last_totp_time = last_totp_time;
+
+ if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen
+ authorize_packet<GetTOTP, UserAuthorize>(gt, user_temporary_password, device);
+ }
+ auto resp = GetTOTP::CommandTransaction::run(device, gt);
+ return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
+ } else {
+ auto gt = get_payload<stick10_08::GetTOTP>();
+ strcpyT(gt.temporary_user_password, user_temporary_password);
+ gt.slot_number = slot_number;
+ auto resp = stick10_08::GetTOTP::CommandTransaction::run(device, gt);
+ return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
+ }
+ return "";
+ }
+
+ bool NitrokeyManager::erase_slot(uint8_t slot_number, const char *temporary_password) {
+ if (is_authorization_command_supported()){
+ auto p = get_payload<EraseSlot>();
+ p.slot_number = slot_number;
+ authorize_packet<EraseSlot, Authorize>(p, temporary_password, device);
+ auto resp = EraseSlot::CommandTransaction::run(device,p);
+ } else {
+ auto p = get_payload<stick10_08::EraseSlot>();
+ p.slot_number = slot_number;
+ strcpyT(p.temporary_admin_password, temporary_password);
+ auto resp = stick10_08::EraseSlot::CommandTransaction::run(device,p);
+ }
+ return true;
+ }
+
+ bool NitrokeyManager::erase_hotp_slot(uint8_t slot_number, const char *temporary_password) {
+ if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ slot_number = get_internal_slot_number_for_hotp(slot_number);
+ return erase_slot(slot_number, temporary_password);
+ }
+
+ bool NitrokeyManager::erase_totp_slot(uint8_t slot_number, const char *temporary_password) {
+ if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ slot_number = get_internal_slot_number_for_totp(slot_number);
+ return erase_slot(slot_number, temporary_password);
+ }
+
+ template <typename T, typename U>
+ void vector_copy_ranged(T& dest, std::vector<U> &vec, size_t begin, size_t elements_to_copy){
+ const size_t d_size = sizeof(dest);
+ if(d_size < elements_to_copy){
+ throw TargetBufferSmallerThanSource(elements_to_copy, d_size);
+ }
+ std::fill(dest, dest+d_size, 0);
+ std::copy(vec.begin() + begin, vec.begin() +begin + elements_to_copy, dest);
+ }
+
+ template <typename T, typename U>
+ void vector_copy(T& dest, std::vector<U> &vec){
+ const size_t d_size = sizeof(dest);
+ if(d_size < vec.size()){
+ throw TargetBufferSmallerThanSource(vec.size(), d_size);
+ }
+ std::fill(dest, dest+d_size, 0);
+ std::copy(vec.begin(), vec.end(), dest);
+ }
+
+ bool NitrokeyManager::write_HOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
+ bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
+ const char *temporary_password) {
+ if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+
+ int internal_slot_number = get_internal_slot_number_for_hotp(slot_number);
+ if (is_authorization_command_supported()){
+ write_HOTP_slot_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID,
+ token_ID, temporary_password);
+ } else {
+ write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID,
+ token_ID, temporary_password);
+ }
+ return true;
+ }
+
+ void NitrokeyManager::write_HOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret,
+ uint64_t hotp_counter, bool use_8_digits, bool use_enter,
+ bool use_tokenID, const char *token_ID, const char *temporary_password) {
+ auto payload = get_payload<WriteToHOTPSlot>();
+ payload.slot_number = slot_number;
+ auto secret_bin = misc::hex_string_to_byte(secret);
+ vector_copy(payload.slot_secret, secret_bin);
+ strcpyT(payload.slot_name, slot_name);
+ strcpyT(payload.slot_token_id, token_ID);
+ switch (device->get_device_model() ){
+ case DeviceModel::PRO: {
+ payload.slot_counter = hotp_counter;
+ break;
+ }
+ case DeviceModel::STORAGE: {
+ string counter = to_string(hotp_counter);
+ strcpyT(payload.slot_counter_s, counter.c_str());
+ break;
+ }
+ default:
+ LOG(string(__FILE__) + to_string(__LINE__) +
+ string(__FUNCTION__) + string(" Unhandled device model for HOTP")
+ , Loglevel::DEBUG);
+ break;
+ }
+ payload.use_8_digits = use_8_digits;
+ payload.use_enter = use_enter;
+ payload.use_tokenID = use_tokenID;
+
+ authorize_packet<WriteToHOTPSlot, Authorize>(payload, temporary_password, device);
+
+ auto resp = WriteToHOTPSlot::CommandTransaction::run(device, payload);
+ }
+
+ bool NitrokeyManager::write_TOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
+ bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
+ const char *temporary_password) {
+ if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ int internal_slot_number = get_internal_slot_number_for_totp(slot_number);
+
+ if (is_authorization_command_supported()){
+ write_TOTP_slot_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID,
+ token_ID, temporary_password);
+ } else {
+ write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID,
+ token_ID, temporary_password);
+ }
+
+ return true;
+ }
+
+ void NitrokeyManager::write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name,
+ const char *secret,
+ uint64_t counter_or_interval, bool use_8_digits, bool use_enter,
+ bool use_tokenID, const char *token_ID,
+ const char *temporary_password) const {
+
+ auto payload2 = get_payload<stick10_08::SendOTPData>();
+ strcpyT(payload2.temporary_admin_password, temporary_password);
+ strcpyT(payload2.data, slot_name);
+ payload2.setTypeName();
+ stick10_08::SendOTPData::CommandTransaction::run(device, payload2);
+
+ payload2.setTypeSecret();
+ payload2.id = 0;
+ auto secret_bin = misc::hex_string_to_byte(secret);
+ auto remaining_secret_length = secret_bin.size();
+ const auto maximum_OTP_secret_size = 40;
+ if(remaining_secret_length > maximum_OTP_secret_size){
+ throw TargetBufferSmallerThanSource(remaining_secret_length, maximum_OTP_secret_size);
+ }
+
+ while (remaining_secret_length>0){
+ const auto bytesToCopy = std::min(sizeof(payload2.data), remaining_secret_length);
+ const auto start = secret_bin.size() - remaining_secret_length;
+ memset(payload2.data, 0, sizeof(payload2.data));
+ vector_copy_ranged(payload2.data, secret_bin, start, bytesToCopy);
+ stick10_08::SendOTPData::CommandTransaction::run(device, payload2);
+ remaining_secret_length -= bytesToCopy;
+ payload2.id++;
+ }
+
+ auto payload = get_payload<stick10_08::WriteToOTPSlot>();
+ strcpyT(payload.temporary_admin_password, temporary_password);
+ strcpyT(payload.slot_token_id, token_ID);
+ payload.use_8_digits = use_8_digits;
+ payload.use_enter = use_enter;
+ payload.use_tokenID = use_tokenID;
+ payload.slot_counter_or_interval = counter_or_interval;
+ payload.slot_number = internal_slot_number;
+ stick10_08::WriteToOTPSlot::CommandTransaction::run(device, payload);
+ }
+
+ void NitrokeyManager::write_TOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret,
+ uint16_t time_window, bool use_8_digits, bool use_enter,
+ bool use_tokenID, const char *token_ID, const char *temporary_password) {
+ auto payload = get_payload<WriteToTOTPSlot>();
+ payload.slot_number = slot_number;
+ auto secret_bin = misc::hex_string_to_byte(secret);
+ vector_copy(payload.slot_secret, secret_bin);
+ strcpyT(payload.slot_name, slot_name);
+ strcpyT(payload.slot_token_id, token_ID);
+ payload.slot_interval = time_window; //FIXME naming
+ payload.use_8_digits = use_8_digits;
+ payload.use_enter = use_enter;
+ payload.use_tokenID = use_tokenID;
+
+ authorize_packet<WriteToTOTPSlot, Authorize>(payload, temporary_password, device);
+
+ auto resp = WriteToTOTPSlot::CommandTransaction::run(device, payload);
+ }
+
+ char * NitrokeyManager::get_totp_slot_name(uint8_t slot_number) {
+ if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ slot_number = get_internal_slot_number_for_totp(slot_number);
+ return get_slot_name(slot_number);
+ }
+ char * NitrokeyManager::get_hotp_slot_name(uint8_t slot_number) {
+ if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ slot_number = get_internal_slot_number_for_hotp(slot_number);
+ return get_slot_name(slot_number);
+ }
+
+ static const int max_string_field_length = 2*1024; //storage's status string is ~1k
+
+ char * NitrokeyManager::get_slot_name(uint8_t slot_number) {
+ auto payload = get_payload<GetSlotName>();
+ payload.slot_number = slot_number;
+ auto resp = GetSlotName::CommandTransaction::run(device, payload);
+ return strndup((const char *) resp.data().slot_name, max_string_field_length);
+ }
+
+ bool NitrokeyManager::first_authenticate(const char *pin, const char *temporary_password) {
+ auto authreq = get_payload<FirstAuthenticate>();
+ strcpyT(authreq.card_password, pin);
+ strcpyT(authreq.temporary_password, temporary_password);
+ FirstAuthenticate::CommandTransaction::run(device, authreq);
+ return true;
+ }
+
+ bool NitrokeyManager::set_time(uint64_t time) {
+ auto p = get_payload<SetTime>();
+ p.reset = 1;
+ p.time = time;
+ SetTime::CommandTransaction::run(device, p);
+ return false;
+ }
+
+ void NitrokeyManager::set_time_soft(uint64_t time) {
+ auto p = get_payload<SetTime>();
+ p.reset = 0;
+ p.time = time;
+ SetTime::CommandTransaction::run(device, p);
+ }
+
+ bool NitrokeyManager::get_time(uint64_t time) {
+ set_time_soft(time);
+ return true;
+ }
+
+ void NitrokeyManager::change_user_PIN(const char *current_PIN, const char *new_PIN) {
+ change_PIN_general<ChangeUserPin, PasswordKind::User>(current_PIN, new_PIN);
+ }
+
+ void NitrokeyManager::change_admin_PIN(const char *current_PIN, const char *new_PIN) {
+ change_PIN_general<ChangeAdminPin, PasswordKind::Admin>(current_PIN, new_PIN);
+ }
+
+ template <typename ProCommand, PasswordKind StoKind>
+ void NitrokeyManager::change_PIN_general(const char *current_PIN, const char *new_PIN) {
+ switch (device->get_device_model()){
+ case DeviceModel::PRO:
+ {
+ auto p = get_payload<ProCommand>();
+ strcpyT(p.old_pin, current_PIN);
+ strcpyT(p.new_pin, new_PIN);
+ ProCommand::CommandTransaction::run(device, p);
+ }
+ break;
+ //in Storage change admin/user pin is divided to two commands with 20 chars field len
+ case DeviceModel::STORAGE:
+ {
+ auto p = get_payload<ChangeAdminUserPin20Current>();
+ strcpyT(p.password, current_PIN);
+ p.set_kind(StoKind);
+ auto p2 = get_payload<ChangeAdminUserPin20New>();
+ strcpyT(p2.password, new_PIN);
+ p2.set_kind(StoKind);
+ ChangeAdminUserPin20Current::CommandTransaction::run(device, p);
+ ChangeAdminUserPin20New::CommandTransaction::run(device, p2);
+ }
+ break;
+ }
+
+ }
+
+ void NitrokeyManager::enable_password_safe(const char *user_pin) {
+ //The following command will cancel enabling PWS if it is not supported
+ auto a = get_payload<IsAESSupported>();
+ strcpyT(a.user_password, user_pin);
+ IsAESSupported::CommandTransaction::run(device, a);
+
+ auto p = get_payload<EnablePasswordSafe>();
+ strcpyT(p.user_password, user_pin);
+ EnablePasswordSafe::CommandTransaction::run(device, p);
+ }
+
+ vector <uint8_t> NitrokeyManager::get_password_safe_slot_status() {
+ auto responsePayload = GetPasswordSafeSlotStatus::CommandTransaction::run(device);
+ vector<uint8_t> v = vector<uint8_t>(responsePayload.data().password_safe_status,
+ responsePayload.data().password_safe_status
+ + sizeof(responsePayload.data().password_safe_status));
+ return v;
+ }
+
+ uint8_t NitrokeyManager::get_user_retry_count() {
+ if(device->get_device_model() == DeviceModel::STORAGE){
+ stick20::GetDeviceStatus::CommandTransaction::run(device);
+ }
+ auto response = GetUserPasswordRetryCount::CommandTransaction::run(device);
+ return response.data().password_retry_count;
+ }
+
+ uint8_t NitrokeyManager::get_admin_retry_count() {
+ if(device->get_device_model() == DeviceModel::STORAGE){
+ stick20::GetDeviceStatus::CommandTransaction::run(device);
+ }
+ auto response = GetPasswordRetryCount::CommandTransaction::run(device);
+ return response.data().password_retry_count;
+ }
+
+ void NitrokeyManager::lock_device() {
+ LockDevice::CommandTransaction::run(device);
+ }
+
+ char * NitrokeyManager::get_password_safe_slot_name(uint8_t slot_number) {
+ if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ auto p = get_payload<GetPasswordSafeSlotName>();
+ p.slot_number = slot_number;
+ auto response = GetPasswordSafeSlotName::CommandTransaction::run(device, p);
+ return strndup((const char *) response.data().slot_name, max_string_field_length);
+ }
+
+ bool NitrokeyManager::is_valid_password_safe_slot_number(uint8_t slot_number) const { return slot_number < 16; }
+
+ char * NitrokeyManager::get_password_safe_slot_login(uint8_t slot_number) {
+ if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ auto p = get_payload<GetPasswordSafeSlotLogin>();
+ p.slot_number = slot_number;
+ auto response = GetPasswordSafeSlotLogin::CommandTransaction::run(device, p);
+ return strndup((const char *) response.data().slot_login, max_string_field_length);
+ }
+
+ char * NitrokeyManager::get_password_safe_slot_password(uint8_t slot_number) {
+ if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ auto p = get_payload<GetPasswordSafeSlotPassword>();
+ p.slot_number = slot_number;
+ auto response = GetPasswordSafeSlotPassword::CommandTransaction::run(device, p);
+ return strndup((const char *) response.data().slot_password, max_string_field_length); //FIXME use secure way
+ }
+
+ void NitrokeyManager::write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
+ const char *slot_password) {
+ if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ auto p = get_payload<SetPasswordSafeSlotData>();
+ p.slot_number = slot_number;
+ strcpyT(p.slot_name, slot_name);
+ strcpyT(p.slot_password, slot_password);
+ SetPasswordSafeSlotData::CommandTransaction::run(device, p);
+
+ auto p2 = get_payload<SetPasswordSafeSlotData2>();
+ p2.slot_number = slot_number;
+ strcpyT(p2.slot_login_name, slot_login);
+ SetPasswordSafeSlotData2::CommandTransaction::run(device, p2);
+ }
+
+ void NitrokeyManager::erase_password_safe_slot(uint8_t slot_number) {
+ if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
+ auto p = get_payload<ErasePasswordSafeSlot>();
+ p.slot_number = slot_number;
+ ErasePasswordSafeSlot::CommandTransaction::run(device, p);
+ }
+
+ void NitrokeyManager::user_authenticate(const char *user_password, const char *temporary_password) {
+ auto p = get_payload<UserAuthenticate>();
+ strcpyT(p.card_password, user_password);
+ strcpyT(p.temporary_password, temporary_password);
+ UserAuthenticate::CommandTransaction::run(device, p);
+ }
+
+ void NitrokeyManager::build_aes_key(const char *admin_password) {
+ switch (device->get_device_model()) {
+ case DeviceModel::PRO: {
+ auto p = get_payload<BuildAESKey>();
+ strcpyT(p.admin_password, admin_password);
+ BuildAESKey::CommandTransaction::run(device, p);
+ break;
+ }
+ case DeviceModel::STORAGE : {
+ auto p = get_payload<stick20::CreateNewKeys>();
+ strcpyT(p.password, admin_password);
+ p.set_defaults();
+ stick20::CreateNewKeys::CommandTransaction::run(device, p);
+ break;
+ }
+ }
+ }
+
+ void NitrokeyManager::factory_reset(const char *admin_password) {
+ auto p = get_payload<FactoryReset>();
+ strcpyT(p.admin_password, admin_password);
+ FactoryReset::CommandTransaction::run(device, p);
+ }
+
+ void NitrokeyManager::unlock_user_password(const char *admin_password, const char *new_user_password) {
+ switch (device->get_device_model()){
+ case DeviceModel::PRO: {
+ auto p = get_payload<stick10::UnlockUserPassword>();
+ strcpyT(p.admin_password, admin_password);
+ strcpyT(p.user_new_password, new_user_password);
+ stick10::UnlockUserPassword::CommandTransaction::run(device, p);
+ break;
+ }
+ case DeviceModel::STORAGE : {
+ auto p2 = get_payload<ChangeAdminUserPin20Current>();
+ p2.set_defaults();
+ strcpyT(p2.password, admin_password);
+ ChangeAdminUserPin20Current::CommandTransaction::run(device, p2);
+ auto p3 = get_payload<stick20::UnlockUserPin>();
+ p3.set_defaults();
+ strcpyT(p3.password, new_user_password);
+ stick20::UnlockUserPin::CommandTransaction::run(device, p3);
+ break;
+ }
+ }
+ }
+
+
+ void NitrokeyManager::write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
+ bool delete_user_password, const char *admin_temporary_password) {
+ auto p = get_payload<stick10_08::WriteGeneralConfig>();
+ p.numlock = numlock;
+ p.capslock = capslock;
+ p.scrolllock = scrolllock;
+ p.enable_user_password = static_cast<uint8_t>(enable_user_password ? 1 : 0);
+ p.delete_user_password = static_cast<uint8_t>(delete_user_password ? 1 : 0);
+ if (is_authorization_command_supported()){
+ authorize_packet<stick10_08::WriteGeneralConfig, Authorize>(p, admin_temporary_password, device);
+ } else {
+ strcpyT(p.temporary_admin_password, admin_temporary_password);
+ }
+ stick10_08::WriteGeneralConfig::CommandTransaction::run(device, p);
+ }
+
+ vector<uint8_t> NitrokeyManager::read_config() {
+ auto responsePayload = GetStatus::CommandTransaction::run(device);
+ vector<uint8_t> v = vector<uint8_t>(responsePayload.data().general_config,
+ responsePayload.data().general_config+sizeof(responsePayload.data().general_config));
+ return v;
+ }
+
+ bool NitrokeyManager::is_authorization_command_supported(){
+ //authorization command is supported for versions equal or below:
+ auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({
+ {DeviceModel::PRO, 7},
+ {DeviceModel::STORAGE, 53},
+ });
+ return get_minor_firmware_version() <= m[device->get_device_model()];
+ }
+
+ bool NitrokeyManager::is_320_OTP_secret_supported(){
+ // 320 bit OTP secret is supported by version bigger or equal to:
+ auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({
+ {DeviceModel::PRO, 8},
+ {DeviceModel::STORAGE, 54},
+ });
+ return get_minor_firmware_version() >= m[device->get_device_model()];
+ }
+
+ DeviceModel NitrokeyManager::get_connected_device_model() const{
+ if (device == nullptr){
+ throw DeviceNotConnected("device not connected");
+ }
+ return device->get_device_model();
+ }
+
+ bool NitrokeyManager::is_smartcard_in_use(){
+ try{
+ stick20::CheckSmartcardUsage::CommandTransaction::run(device);
+ }
+ catch(const CommandFailedException & e){
+ return e.reason_smartcard_busy();
+ }
+ return false;
+ }
+
+ uint8_t NitrokeyManager::get_minor_firmware_version(){
+ switch(device->get_device_model()){
+ case DeviceModel::PRO:{
+ auto status_p = GetStatus::CommandTransaction::run(device);
+ return status_p.data().firmware_version_st.minor; //7 or 8
+ }
+ case DeviceModel::STORAGE:{
+ auto status = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ auto test_firmware = status.data().versionInfo.build_iteration != 0;
+ if (test_firmware)
+ LOG("Development firmware detected. Increasing minor version number.", nitrokey::log::Loglevel::WARNING);
+ return status.data().versionInfo.minor + (test_firmware? 1 : 0);
+ }
+ }
+ return 0;
+ }
+ uint8_t NitrokeyManager::get_major_firmware_version(){
+ switch(device->get_device_model()){
+ case DeviceModel::PRO:{
+ auto status_p = GetStatus::CommandTransaction::run(device);
+ return status_p.data().firmware_version_st.major; //0
+ }
+ case DeviceModel::STORAGE:{
+ auto status = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return status.data().versionInfo.major;
+ }
+ }
+ return 0;
+ }
+
+ bool NitrokeyManager::is_AES_supported(const char *user_password) {
+ auto a = get_payload<IsAESSupported>();
+ strcpyT(a.user_password, user_password);
+ IsAESSupported::CommandTransaction::run(device, a);
+ return true;
+ }
+
+ //storage commands
+
+ void NitrokeyManager::send_startup(uint64_t seconds_from_epoch){
+ auto p = get_payload<stick20::SendStartup>();
+// p.set_defaults(); //set current time
+ p.localtime = seconds_from_epoch;
+ stick20::SendStartup::CommandTransaction::run(device, p);
+ }
+
+ void NitrokeyManager::unlock_encrypted_volume(const char* user_pin){
+ misc::execute_password_command<stick20::EnableEncryptedPartition>(device, user_pin);
+ }
+
+ void NitrokeyManager::unlock_hidden_volume(const char* hidden_volume_password) {
+ misc::execute_password_command<stick20::EnableHiddenEncryptedPartition>(device, hidden_volume_password);
+ }
+
+ void NitrokeyManager::set_encrypted_volume_read_only(const char* admin_pin) {
+ misc::execute_password_command<stick20::SetEncryptedVolumeReadOnly>(device, admin_pin);
+ }
+
+ void NitrokeyManager::set_encrypted_volume_read_write(const char* admin_pin) {
+ misc::execute_password_command<stick20::SetEncryptedVolumeReadWrite>(device, admin_pin);
+ }
+
+ //TODO check is encrypted volume unlocked before execution
+ //if not return library exception
+ void NitrokeyManager::create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
+ const char *hidden_volume_password) {
+ auto p = get_payload<stick20::SetupHiddenVolume>();
+ p.SlotNr_u8 = slot_nr;
+ p.StartBlockPercent_u8 = start_percent;
+ p.EndBlockPercent_u8 = end_percent;
+ strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password);
+ stick20::SetupHiddenVolume::CommandTransaction::run(device, p);
+ }
+
+ void NitrokeyManager::set_unencrypted_read_only_admin(const char* admin_pin) {
+ //from v0.49, v0.52+ it needs Admin PIN
+ if (set_unencrypted_volume_rorw_pin_type_user()){
+ LOG("set_unencrypted_read_only_admin is not supported for this version of Storage device. "
+ "Please update firmware to v0.52+. Doing nothing.", nitrokey::log::Loglevel::WARNING);
+ return;
+ }
+ misc::execute_password_command<stick20::SetUnencryptedVolumeReadOnlyAdmin>(device, admin_pin);
+ }
+
+ void NitrokeyManager::set_unencrypted_read_only(const char *user_pin) {
+ //until v0.48 (incl. v0.50 and v0.51) User PIN was sufficient
+ LOG("set_unencrypted_read_only is deprecated. Use set_unencrypted_read_only_admin instead.",
+ nitrokey::log::Loglevel::WARNING);
+ if (!set_unencrypted_volume_rorw_pin_type_user()){
+ LOG("set_unencrypted_read_only is not supported for this version of Storage device. Doing nothing.",
+ nitrokey::log::Loglevel::WARNING);
+ return;
+ }
+ misc::execute_password_command<stick20::SendSetReadonlyToUncryptedVolume>(device, user_pin);
+ }
+
+ void NitrokeyManager::set_unencrypted_read_write_admin(const char* admin_pin) {
+ //from v0.49, v0.52+ it needs Admin PIN
+ if (set_unencrypted_volume_rorw_pin_type_user()){
+ LOG("set_unencrypted_read_write_admin is not supported for this version of Storage device. "
+ "Please update firmware to v0.52+. Doing nothing.", nitrokey::log::Loglevel::WARNING);
+ return;
+ }
+ misc::execute_password_command<stick20::SetUnencryptedVolumeReadWriteAdmin>(device, admin_pin);
+ }
+
+ void NitrokeyManager::set_unencrypted_read_write(const char *user_pin) {
+ //until v0.48 (incl. v0.50 and v0.51) User PIN was sufficient
+ LOG("set_unencrypted_read_write is deprecated. Use set_unencrypted_read_write_admin instead.",
+ nitrokey::log::Loglevel::WARNING);
+ if (!set_unencrypted_volume_rorw_pin_type_user()){
+ LOG("set_unencrypted_read_write is not supported for this version of Storage device. Doing nothing.",
+ nitrokey::log::Loglevel::WARNING);
+ return;
+ }
+ misc::execute_password_command<stick20::SendSetReadwriteToUncryptedVolume>(device, user_pin);
+ }
+
+ bool NitrokeyManager::set_unencrypted_volume_rorw_pin_type_user(){
+ auto minor_firmware_version = get_minor_firmware_version();
+ return minor_firmware_version <= 48 || minor_firmware_version == 50 || minor_firmware_version == 51;
+ }
+
+ void NitrokeyManager::export_firmware(const char* admin_pin) {
+ misc::execute_password_command<stick20::ExportFirmware>(device, admin_pin);
+ }
+
+ void NitrokeyManager::enable_firmware_update(const char* firmware_pin) {
+ misc::execute_password_command<stick20::EnableFirmwareUpdate>(device, firmware_pin);
+ }
+
+ void NitrokeyManager::clear_new_sd_card_warning(const char* admin_pin) {
+ misc::execute_password_command<stick20::SendClearNewSdCardFound>(device, admin_pin);
+ }
+
+ void NitrokeyManager::fill_SD_card_with_random_data(const char* admin_pin) {
+ auto p = get_payload<stick20::FillSDCardWithRandomChars>();
+ p.set_defaults();
+ strcpyT(p.admin_pin, admin_pin);
+ stick20::FillSDCardWithRandomChars::CommandTransaction::run(device, p);
+ }
+
+ void NitrokeyManager::change_update_password(const char* current_update_password, const char* new_update_password) {
+ auto p = get_payload<stick20::ChangeUpdatePassword>();
+ strcpyT(p.current_update_password, current_update_password);
+ strcpyT(p.new_update_password, new_update_password);
+ stick20::ChangeUpdatePassword::CommandTransaction::run(device, p);
+ }
+
+ char * NitrokeyManager::get_status_storage_as_string(){
+ auto p = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return strndup(p.data().dissect().c_str(), max_string_field_length);
+ }
+
+ stick20::DeviceConfigurationResponsePacket::ResponsePayload NitrokeyManager::get_status_storage(){
+ auto p = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return p.data();
+ }
+
+ char * NitrokeyManager::get_SD_usage_data_as_string(){
+ auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device);
+ return strndup(p.data().dissect().c_str(), max_string_field_length);
+ }
+
+ std::pair<uint8_t,uint8_t> NitrokeyManager::get_SD_usage_data(){
+ auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device);
+ return std::make_pair(p.data().WriteLevelMin, p.data().WriteLevelMax);
+ }
+
+ int NitrokeyManager::get_progress_bar_value(){
+ try{
+ stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return -1;
+ }
+ catch (LongOperationInProgressException &e){
+ return e.progress_bar_value;
+ }
+ }
+
+ string NitrokeyManager::get_TOTP_code(uint8_t slot_number, const char *user_temporary_password) {
+ return get_TOTP_code(slot_number, 0, 0, 0, user_temporary_password);
+ }
+
+ /**
+ * Returns ReadSlot structure, describing OTP slot configuration. Always return binary counter -
+ * does the necessary conversion, if needed, to unify the behavior across Pro and Storage.
+ * @private For internal use only
+ * @param slot_number which OTP slot to use (usual format)
+ * @return ReadSlot structure
+ */
+ stick10::ReadSlot::ResponsePayload NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) {
+ auto p = get_payload<stick10::ReadSlot>();
+ p.slot_number = slot_number;
+ p.data_format = stick10::ReadSlot::CounterFormat::BINARY; // ignored for devices other than Storage v0.54+
+ auto data = stick10::ReadSlot::CommandTransaction::run(device, p);
+
+ auto &payload = data.data();
+
+ // if fw <=v0.53 and asked binary - do the conversion from ASCII
+ if (device->get_device_model() == DeviceModel::STORAGE && get_minor_firmware_version() <= 53
+ && is_internal_hotp_slot_number(slot_number))
+ {
+ //convert counter from string to ull
+ auto counter_s = std::string(payload.slot_counter_s, payload.slot_counter_s + sizeof(payload.slot_counter_s));
+ payload.slot_counter = std::stoull(counter_s);
+ }
+
+ return payload;
+ }
+
+ stick10::ReadSlot::ResponsePayload NitrokeyManager::get_TOTP_slot_data(const uint8_t slot_number) {
+ return get_OTP_slot_data(get_internal_slot_number_for_totp(slot_number));
+ }
+
+ stick10::ReadSlot::ResponsePayload NitrokeyManager::get_HOTP_slot_data(const uint8_t slot_number) {
+ return get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number));
+ }
+
+ void NitrokeyManager::lock_encrypted_volume() {
+ misc::execute_password_command<stick20::DisableEncryptedPartition>(device, "");
+ }
+
+ void NitrokeyManager::lock_hidden_volume() {
+ misc::execute_password_command<stick20::DisableHiddenEncryptedPartition>(device, "");
+ }
+
+ uint8_t NitrokeyManager::get_SD_card_size() {
+ auto data = stick20::ProductionTest::CommandTransaction::run(device);
+ return data.data().SD_Card_Size_u8;
+ }
+
+ const string NitrokeyManager::get_current_device_id() const {
+ return current_device_id;
+ }
+
+ void NitrokeyManager::wink(){
+ stick20::Wink::CommandTransaction::run(device);
+ };
+
+ stick20::ProductionTest::ResponsePayload NitrokeyManager::production_info(){
+ auto data = stick20::ProductionTest::CommandTransaction::run(device);
+ return data.data();
+ };
+
+ void NitrokeyManager::enable_firmware_update_pro(const char *firmware_pin) {
+ auto p = get_payload<FirmwareUpdate>();
+ strcpyT(p.firmware_password, firmware_pin);
+ FirmwareUpdate::CommandTransaction::run(device, p);
+ }
+
+ void
+ NitrokeyManager::change_firmware_update_password_pro(const char *firmware_pin_current, const char *firmware_pin_new) {
+ auto p = get_payload<FirmwarePasswordChange>();
+ strcpyT(p.firmware_password_current, firmware_pin_current);
+ strcpyT(p.firmware_password_new, firmware_pin_new);
+ FirmwarePasswordChange::CommandTransaction::run(device, p);
+ }
+
+}