diff options
author | Szczepan Zalega <szczepan@nitrokey.com> | 2020-07-28 12:50:53 +0200 |
---|---|---|
committer | Szczepan Zalega <szczepan@nitrokey.com> | 2020-07-28 12:50:53 +0200 |
commit | a36392dd83def4397d100addf57870ebea5de0e9 (patch) | |
tree | 41aaefa5b44ffc3b879f490c58e008930d5f4fbd | |
parent | dc1cfa6252073ac345412e7df9c5cc0365bb7f11 (diff) | |
download | libnitrokey-a36392dd83def4397d100addf57870ebea5de0e9.tar.gz libnitrokey-a36392dd83def4397d100addf57870ebea5de0e9.tar.bz2 |
Extract OTP features to separate unit
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | NK_C_API.cc | 2 | ||||
-rw-r--r-- | NitrokeyManager.cc | 371 | ||||
-rw-r--r-- | NitrokeyManagerOTP.cc | 327 | ||||
-rw-r--r-- | NitrokeyManagerOTP.h | 6 | ||||
-rw-r--r-- | NitrokeyManagerStorage.cpp | 1 | ||||
-rw-r--r-- | libnitrokey/NitrokeyManager.h | 1 |
7 files changed, 345 insertions, 365 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6793ac9..6e0bcb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ set(SOURCE_FILES NitrokeyManager.cc NitrokeyManagerStorage.cpp NitrokeyManagerStorage.h + NitrokeyManagerOTP.cc + NitrokeyManagerOTP.h NK_C_API.h NK_C_API.cc NK_C_API_helpers.h diff --git a/NK_C_API.cc b/NK_C_API.cc index 2f5a0cc..9dd0837 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -37,9 +37,9 @@ using namespace nitrokey; const uint8_t NK_PWS_SLOT_COUNT = PWS_SLOT_COUNT; uint8_t NK_last_command_status = 0; -static const int max_string_field_length = 100; #include "NK_C_API_helpers.h" +#include "NitrokeyManagerOTP.h" #ifdef __cplusplus extern "C" { diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 3e0b5f6..cde559b 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -19,25 +19,23 @@ * SPDX-License-Identifier: LGPL-3.0 */ -#include <cstring> -#include <iostream> #include "libnitrokey/NitrokeyManager.h" +#include "NitrokeyManagerOTP.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 <algorithm> +#include <cstring> #include <functional> +#include <iostream> +#include <mutex> #include <stick10_commands.h> +#include <stick20_commands.h> +#include <unordered_map> std::mutex nitrokey::proto::send_receive_mtx; namespace nitrokey{ -constexpr int max_string_field_length = 2*1024; //storage's status string is ~1k - std::mutex mex_dev_com_manager; using nitrokey::misc::strcpyT; @@ -420,265 +418,7 @@ using nitrokey::misc::strcpyT; 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::LIBREM: - 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); - } - - - 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); - } + // 15 bool NitrokeyManager::first_authenticate(const char *pin, const char *temporary_password) { auto authreq = get_payload<FirstAuthenticate>(); @@ -688,26 +428,6 @@ using nitrokey::misc::strcpyT; 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); } @@ -888,30 +608,6 @@ using nitrokey::misc::strcpyT; } } - - 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>({ @@ -922,16 +618,6 @@ using nitrokey::misc::strcpyT; 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::LIBREM, 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"); @@ -988,47 +674,6 @@ using nitrokey::misc::strcpyT; return true; } - - 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)); - } - - const string NitrokeyManager::get_current_device_id() const { return current_device_id; } diff --git a/NitrokeyManagerOTP.cc b/NitrokeyManagerOTP.cc new file mode 100644 index 0000000..93f2188 --- /dev/null +++ b/NitrokeyManagerOTP.cc @@ -0,0 +1,327 @@ +#include "NitrokeyManagerOTP.h" +using nitrokey::misc::strcpyT; + +std::string getFilledOTPCode(uint32_t code, bool use_8_digits){ + std::stringstream s; + s << std::right << std::setw(use_8_digits ? 8 : 6) << std::setfill('0') << code; + return s.str(); +} +std::string nitrokey::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 nitrokey::NitrokeyManager::is_internal_hotp_slot_number(uint8_t slot_number) const { return slot_number < 0x20; } +bool nitrokey::NitrokeyManager::is_valid_hotp_slot_number(uint8_t slot_number) const { return slot_number < 3; } +bool nitrokey::NitrokeyManager::is_valid_totp_slot_number(uint8_t slot_number) const { return slot_number < 0x10-1; } +uint8_t nitrokey::NitrokeyManager::get_internal_slot_number_for_totp(uint8_t slot_number) const { return (uint8_t) (0x20 + slot_number); +} +uint8_t nitrokey::NitrokeyManager::get_internal_slot_number_for_hotp(uint8_t slot_number) const { return (uint8_t) (0x10 + slot_number); } +std::string nitrokey::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 nitrokey::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 nitrokey::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 nitrokey::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, std::size_t begin, std::size_t elements_to_copy){ + const std::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 std::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 nitrokey::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 nitrokey::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::LIBREM: + 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 nitrokey::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 nitrokey::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 nitrokey::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 * nitrokey::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 *nitrokey::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); +} +char *nitrokey::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 nitrokey::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 nitrokey::NitrokeyManager::set_time_soft(uint64_t time) { + auto p = get_payload<SetTime>(); + p.reset = 0; + p.time = time; + SetTime::CommandTransaction::run(device, p); +} +bool nitrokey::NitrokeyManager::get_time(uint64_t time) { + set_time_soft(time); + return true; +} +void nitrokey::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); +} +std::vector<uint8_t> nitrokey::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 nitrokey::NitrokeyManager::is_320_OTP_secret_supported(){ + // 320 bit OTP secret is supported by version bigger or equal to: + auto m = unordered_map<DeviceModel , int, EnumClassHash>({ + {DeviceModel::PRO, 8}, + {DeviceModel::LIBREM, 8}, + {DeviceModel::STORAGE, 54}, + }); + return get_minor_firmware_version() >= m[device->get_device_model()]; +} +std::string nitrokey::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 + */ +nitrokey::proto::stick10::ReadSlot::ResponsePayload +nitrokey::NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) { + auto p = get_payload<ReadSlot>(); + p.slot_number = slot_number; + p.data_format = 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 = string(payload.slot_counter_s, payload.slot_counter_s + sizeof(payload.slot_counter_s)); + payload.slot_counter = stoull(counter_s); + } + + return payload; +} + nitrokey::proto::stick10::ReadSlot::ResponsePayload nitrokey::NitrokeyManager::get_TOTP_slot_data(const uint8_t slot_number) { + return get_OTP_slot_data(get_internal_slot_number_for_totp(slot_number)); + } + nitrokey::proto::stick10::ReadSlot::ResponsePayload nitrokey::NitrokeyManager::get_HOTP_slot_data(const uint8_t slot_number) { + return get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number)); + }
\ No newline at end of file diff --git a/NitrokeyManagerOTP.h b/NitrokeyManagerOTP.h new file mode 100644 index 0000000..503fef7 --- /dev/null +++ b/NitrokeyManagerOTP.h @@ -0,0 +1,6 @@ +#ifndef LIBNITROKEY_NITROKEYMANAGEROTP_H +#define LIBNITROKEY_NITROKEYMANAGEROTP_H + +#include "NitrokeyManager.h" + +#endif // LIBNITROKEY_NITROKEYMANAGEROTP_H diff --git a/NitrokeyManagerStorage.cpp b/NitrokeyManagerStorage.cpp index 6f37d88..6814e8b 100644 --- a/NitrokeyManagerStorage.cpp +++ b/NitrokeyManagerStorage.cpp @@ -4,7 +4,6 @@ namespace nitrokey{ using nitrokey::misc::strcpyT; -constexpr int max_string_field_length = 2*1024; //storage's status string is ~1k //storage commands diff --git a/libnitrokey/NitrokeyManager.h b/libnitrokey/NitrokeyManager.h index ba61793..20db98d 100644 --- a/libnitrokey/NitrokeyManager.h +++ b/libnitrokey/NitrokeyManager.h @@ -49,6 +49,7 @@ typename T::CommandPayload get_payload(){ } #include "nk_strndup.h" +constexpr int max_string_field_length = 2*1024; //storage's status string is ~1k class NitrokeyManager { public: |