diff options
Diffstat (limited to 'libnitrokey-v3.4/NitrokeyManager.cc')
-rw-r--r-- | libnitrokey-v3.4/NitrokeyManager.cc | 1149 |
1 files changed, 1149 insertions, 0 deletions
diff --git a/libnitrokey-v3.4/NitrokeyManager.cc b/libnitrokey-v3.4/NitrokeyManager.cc new file mode 100644 index 0000000..a950e4b --- /dev/null +++ b/libnitrokey-v3.4/NitrokeyManager.cc @@ -0,0 +1,1149 @@ +/* + * 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 <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<std::string> NitrokeyManager::list_devices(){ + std::lock_guard<std::mutex> lock(mex_dev_com_manager); + + auto p = make_shared<Stick20>(); + return p->enumerate(); // make static + } + + 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; + auto d = make_shared<Stick20>(); + const auto v = d->enumerate(); + LOGD1("Discovering IDs"); + for (auto & p: v){ + 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 p = make_shared<Stick20>(); + 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_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, 999}, + }); + return get_minor_firmware_version() <= m[device->get_device_model()]; + } + + bool NitrokeyManager::is_320_OTP_secret_supported(){ + //authorization command is supported for versions equal or below: + auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({ + {DeviceModel::PRO, 8}, + {DeviceModel::STORAGE, 999}, + }); + 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; + } + + int 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; + } + int 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); + } + + stick10::ReadSlot::ResponsePayload NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) { + auto p = get_payload<stick10::ReadSlot>(); + p.slot_number = slot_number; + auto data = stick10::ReadSlot::CommandTransaction::run(device, p); + return data.data(); + } + + 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) { + auto slot_data = get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number)); + if (device->get_device_model() == DeviceModel::STORAGE){ + //convert counter from string to ull + auto counter_s = std::string(slot_data.slot_counter_s, slot_data.slot_counter_s+sizeof(slot_data.slot_counter_s)); + slot_data.slot_counter = std::stoull(counter_s); + } + return slot_data; + } + + 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(); + }; + +} |