diff options
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | NK_C_API.cc | 874 | ||||
-rw-r--r-- | NK_C_API.h | 930 | ||||
-rw-r--r-- | NitrokeyManager.cc | 7 | ||||
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | device.cc | 6 | ||||
m--------- | hidapi | 0 | ||||
-rw-r--r-- | include/command.h | 2 | ||||
-rw-r--r-- | include/device_proto.h | 21 | ||||
-rw-r--r-- | include/dissect.h | 4 | ||||
-rw-r--r-- | include/misc.h | 2 | ||||
-rw-r--r-- | include/stick10_commands.h | 10 | ||||
-rw-r--r-- | include/stick10_commands_0.8.h | 6 | ||||
-rw-r--r-- | include/stick20_commands.h | 13 | ||||
-rw-r--r-- | libnitrokey.pro | 74 | ||||
-rw-r--r-- | misc.cc | 14 | ||||
-rw-r--r-- | unittest/conftest.py | 18 | ||||
-rw-r--r-- | unittest/constants.py | 24 | ||||
-rw-r--r-- | unittest/requirements.txt | 1 | ||||
-rw-r--r-- | unittest/test_issues.cc | 4 | ||||
-rw-r--r-- | unittest/test_pro.py | 153 | ||||
-rw-r--r-- | unittest/test_storage.py | 36 |
22 files changed, 1186 insertions, 1038 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6741b18..11882f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,8 @@ +# https://cmake.org/pipermail/cmake/2011-May/044166.html + IF(NOT DEFINED CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS) + SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON) + ENDIF() + cmake_minimum_required(VERSION 3.1) IF (UNIX) OPTION(USE_CLANG "Use CLang" FALSE) @@ -111,7 +116,7 @@ install (FILES ${LIB_INCLUDES} DESTINATION "include") IF (COMPILE_TESTS) include_directories(unittest/Catch/include) - add_library(catch SHARED unittest/catch_main.cpp ) + add_library(catch STATIC unittest/catch_main.cpp ) add_executable (test_C_API unittest/test_C_API.cpp) target_link_libraries (test_C_API ${EXTRA_LIBS} ${LIBNAME}-log catch) diff --git a/NK_C_API.cc b/NK_C_API.cc index 0e3fa1f..bac11b3 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -5,6 +5,19 @@ #include "include/LibraryException.h" #include "include/cxx_semantics.h" +#ifdef _MSC_VER +#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 namespace nitrokey; static uint8_t NK_last_command_status = 0; @@ -76,434 +89,443 @@ uint8_t get_without_result(T func){ catch (LibraryException & libraryException){ NK_last_command_status = libraryException.exception_id(); } - return NK_last_command_status; -} - - -extern "C" -{ -NK_C_API uint8_t NK_get_last_command_status(){ - auto _copy = NK_last_command_status; - NK_last_command_status = 0; - return _copy; -} - -NK_C_API int NK_login(const char *device_model) { - auto m = NitrokeyManager::instance(); - try { - NK_last_command_status = 0; - return m->connect(device_model); - } - catch (CommandFailedException & commandFailedException){ - NK_last_command_status = commandFailedException.last_command_status; - return commandFailedException.last_command_status; + catch (const InvalidCRCReceived &invalidCRCException){ + ;;; } - catch (std::runtime_error &e){ - cerr << e.what() << endl; - return 0; + catch (const DeviceCommunicationException &deviceException){ + NK_last_command_status = -1; } - return 0; -} - -NK_C_API int NK_logout() { - auto m = NitrokeyManager::instance(); - return get_without_result( [&](){ - m->disconnect(); - }); -} - -NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password){ - auto m = NitrokeyManager::instance(); - return get_without_result( [&](){ - return m->first_authenticate(admin_password, admin_temporary_password); - }); -} - - -NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password){ - auto m = NitrokeyManager::instance(); - return get_without_result( [&](){ - m->user_authenticate(user_password, user_temporary_password); - }); -} - -NK_C_API int NK_factory_reset(const char* admin_password){ - auto m = NitrokeyManager::instance(); - return get_without_result( [&](){ - m->factory_reset(admin_password); - }); -} -NK_C_API int NK_build_aes_key(const char* admin_password){ - auto m = NitrokeyManager::instance(); - return get_without_result( [&](){ - m->build_aes_key(admin_password); - }); -} - -NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) { - auto m = NitrokeyManager::instance(); - return get_without_result( [&](){ - m->unlock_user_password(admin_password, new_user_password); - }); -} - -NK_C_API int NK_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 m = NitrokeyManager::instance(); - return get_without_result( [&](){ - return m->write_config(numlock, capslock, scrolllock, enable_user_password, delete_user_password, admin_temporary_password); - }); -} - - -NK_C_API uint8_t* NK_read_config(){ - auto m = NitrokeyManager::instance(); - return get_with_array_result( [&](){ - auto v = m->read_config(); - return duplicate_vector_and_clear(v); - }); -} - - -void clear_string(std::string &s){ - std::fill(s.begin(), s.end(), ' '); -} - - -NK_C_API const char * NK_status() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - string && s = m->get_status_as_string(); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); -} - -NK_C_API const char * NK_device_serial_number(){ - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - string && s = m->get_serial_number(); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); -} - -NK_C_API const char * NK_get_hotp_code(uint8_t slot_number) { - return NK_get_hotp_code_PIN(slot_number, ""); -} - -NK_C_API const char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password){ - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - string && s = m->get_HOTP_code(slot_number, user_temporary_password); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); -} - -NK_C_API const char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval){ - return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, ""); -} - -NK_C_API const char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval, const char *user_temporary_password){ - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - string && s = m->get_TOTP_code(slot_number, challenge, last_totp_time, last_interval, user_temporary_password); - char * rs = strndup(s.c_str(), max_string_field_length); - clear_string(s); - return rs; - }); -} - -NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]{ - m->erase_hotp_slot(slot_number, temporary_password); - }); -} - -NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]{ - m->erase_totp_slot(slot_number, temporary_password); - }); -} - -NK_C_API int NK_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) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]{ - m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, token_ID, - temporary_password); - }); -} - -NK_C_API int NK_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) { - auto m = NitrokeyManager::instance(); - return get_without_result([&]{ - m->write_TOTP_slot(slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, token_ID, - temporary_password); - }); -} - -NK_C_API const char* NK_get_totp_slot_name(uint8_t slot_number){ - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - const auto slot_name = m->get_totp_slot_name(slot_number); - return slot_name; - }); -} -NK_C_API const char* NK_get_hotp_slot_name(uint8_t slot_number){ - auto m = NitrokeyManager::instance(); - return get_with_string_result([&]() { - const auto slot_name = m->get_hotp_slot_name(slot_number); - return slot_name; - }); -} - -NK_C_API void NK_set_debug(bool state){ - auto m = NitrokeyManager::instance(); - m->set_debug(state); -} - -NK_C_API int NK_totp_set_time(uint64_t time){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->set_time(time); - }); -} - -NK_C_API int NK_totp_get_time(){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->get_time(0); // FIXME check how that should work - }); -} - -NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->change_admin_PIN(current_PIN, new_PIN); - }); -} - -NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->change_user_PIN(current_PIN, new_PIN); - }); -} - -NK_C_API int NK_enable_password_safe(const char *user_pin){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->enable_password_safe(user_pin); - }); -} -NK_C_API uint8_t * NK_get_password_safe_slot_status(){ - auto m = NitrokeyManager::instance(); - return get_with_array_result( [&](){ - auto slot_status = m->get_password_safe_slot_status(); - return duplicate_vector_and_clear(slot_status); - }); - -} - -NK_C_API uint8_t NK_get_user_retry_count(){ - auto m = NitrokeyManager::instance(); - return get_with_result([&](){ - return m->get_user_retry_count(); - }); -} - -NK_C_API uint8_t NK_get_admin_retry_count(){ - auto m = NitrokeyManager::instance(); - return get_with_result([&](){ - return m->get_admin_retry_count(); - }); -} - -NK_C_API int NK_lock_device(){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->lock_device(); - }); -} - -NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - return m->get_password_safe_slot_name(slot_number); - }); -} - -NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - return m->get_password_safe_slot_login(slot_number); - }); -} -NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - return m->get_password_safe_slot_password(slot_number); - }); -} -NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, - const char *slot_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->write_password_safe_slot(slot_number, slot_name, slot_login, slot_password); - }); -} - -NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) { - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->erase_password_safe_slot(slot_number); - }); -} - -NK_C_API int NK_is_AES_supported(const char *user_password) { - auto m = NitrokeyManager::instance(); - return get_with_result([&](){ - return (uint8_t) m->is_AES_supported(user_password); - }); -} - -NK_C_API int NK_login_auto() { - auto m = NitrokeyManager::instance(); - return get_with_result([&](){ - return (uint8_t) m->connect(); - }); -} - -// storage commands - -NK_C_API int NK_send_startup(uint64_t seconds_from_epoch){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->send_startup(seconds_from_epoch); - }); -} - -NK_C_API int NK_unlock_encrypted_volume(const char* user_pin){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->unlock_encrypted_volume(user_pin); - }); -} - -NK_C_API int NK_lock_encrypted_volume(){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->lock_encrypted_volume(); - }); -} - -NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->unlock_hidden_volume(hidden_volume_password); - }); -} - -NK_C_API int NK_lock_hidden_volume(){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->lock_hidden_volume(); - }); -} - -NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, - const char *hidden_volume_password){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->create_hidden_volume( slot_nr, start_percent, end_percent, - hidden_volume_password); - }); -} - -NK_C_API int NK_set_unencrypted_read_only(const char* user_pin){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->set_unencrypted_read_only(user_pin); - }); -} - -NK_C_API int NK_set_unencrypted_read_write(const char* user_pin){ - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->set_unencrypted_read_write(user_pin); - }); -} - -NK_C_API int NK_export_firmware(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->export_firmware(admin_pin) ; - }); -} - -NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->clear_new_sd_card_warning(admin_pin); - }); -} - -NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) { - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->fill_SD_card_with_random_data(admin_pin); - }); -} - -NK_C_API int NK_change_update_password(const char* current_update_password, - const char* new_update_password) { - auto m = NitrokeyManager::instance(); - return get_without_result([&](){ - m->change_update_password(current_update_password, new_update_password); - }); -} - -NK_C_API const char* NK_get_status_storage_as_string() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - return m->get_status_storage_as_string(); - }); -} - -NK_C_API const char* NK_get_SD_usage_data_as_string() { - auto m = NitrokeyManager::instance(); - return get_with_string_result([&](){ - return m->get_SD_usage_data_as_string(); - }); -} - -NK_C_API int NK_get_progress_bar_value() { - auto m = NitrokeyManager::instance(); - return get_with_result([&](){ - return m->get_progress_bar_value(); - }); -} - -NK_C_API int NK_get_major_firmware_version(){ - auto m = NitrokeyManager::instance(); - return get_with_result([&](){ - return m->get_minor_firmware_version(); - }); + return NK_last_command_status; } -} - +#ifdef __cplusplus +extern "C" { +#endif + + NK_C_API uint8_t NK_get_last_command_status() { + auto _copy = NK_last_command_status; + NK_last_command_status = 0; + return _copy; + } + + NK_C_API int NK_login(const char *device_model) { + auto m = NitrokeyManager::instance(); + try { + NK_last_command_status = 0; + return m->connect(device_model); + } + catch (CommandFailedException & commandFailedException) { + NK_last_command_status = commandFailedException.last_command_status; + return commandFailedException.last_command_status; + } + catch (std::runtime_error &e) { + cerr << e.what() << endl; + return 0; + } + return 0; + } + + NK_C_API int NK_logout() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->disconnect(); + }); + } + + NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + return m->first_authenticate(admin_password, admin_temporary_password); + }); + } + + + NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->user_authenticate(user_password, user_temporary_password); + }); + } + + NK_C_API int NK_factory_reset(const char* admin_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->factory_reset(admin_password); + }); + } + NK_C_API int NK_build_aes_key(const char* admin_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->build_aes_key(admin_password); + }); + } + + NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->unlock_user_password(admin_password, new_user_password); + }); + } + + NK_C_API int NK_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 m = NitrokeyManager::instance(); + return get_without_result([&]() { + return m->write_config(numlock, capslock, scrolllock, enable_user_password, delete_user_password, admin_temporary_password); + }); + } + + + NK_C_API uint8_t* NK_read_config() { + auto m = NitrokeyManager::instance(); + return get_with_array_result([&]() { + auto v = m->read_config(); + return duplicate_vector_and_clear(v); + }); + } + + + void clear_string(std::string &s) { + std::fill(s.begin(), s.end(), ' '); + } + + + NK_C_API const char * NK_status() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_status_as_string(); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API const char * NK_device_serial_number() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_serial_number(); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API const char * NK_get_hotp_code(uint8_t slot_number) { + return NK_get_hotp_code_PIN(slot_number, ""); + } + + NK_C_API const char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_HOTP_code(slot_number, user_temporary_password); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API const char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval) { + return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, ""); + } + + NK_C_API const char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval, const char *user_temporary_password) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + string && s = m->get_TOTP_code(slot_number, challenge, last_totp_time, last_interval, user_temporary_password); + char * rs = strndup(s.c_str(), max_string_field_length); + clear_string(s); + return rs; + }); + } + + NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->erase_hotp_slot(slot_number, temporary_password); + }); + } + + NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->erase_totp_slot(slot_number, temporary_password); + }); + } + + NK_C_API int NK_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) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, token_ID, + temporary_password); + }); + } + + NK_C_API int NK_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) { + auto m = NitrokeyManager::instance(); + return get_without_result([&] { + m->write_TOTP_slot(slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, token_ID, + temporary_password); + }); + } + + NK_C_API const char* NK_get_totp_slot_name(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + const auto slot_name = m->get_totp_slot_name(slot_number); + return slot_name; + }); + } + NK_C_API const char* NK_get_hotp_slot_name(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + const auto slot_name = m->get_hotp_slot_name(slot_number); + return slot_name; + }); + } + + NK_C_API void NK_set_debug(bool state) { + auto m = NitrokeyManager::instance(); + m->set_debug(state); + } + + NK_C_API int NK_totp_set_time(uint64_t time) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_time(time); + }); + } + + NK_C_API int NK_totp_get_time() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->get_time(0); // FIXME check how that should work + }); + } + + NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_admin_PIN(current_PIN, new_PIN); + }); + } + + NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_user_PIN(current_PIN, new_PIN); + }); + } + + NK_C_API int NK_enable_password_safe(const char *user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->enable_password_safe(user_pin); + }); + } + NK_C_API uint8_t * NK_get_password_safe_slot_status() { + auto m = NitrokeyManager::instance(); + return get_with_array_result([&]() { + auto slot_status = m->get_password_safe_slot_status(); + return duplicate_vector_and_clear(slot_status); + }); + + } + + NK_C_API uint8_t NK_get_user_retry_count() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_user_retry_count(); + }); + } + + NK_C_API uint8_t NK_get_admin_retry_count() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_admin_retry_count(); + }); + } + + NK_C_API int NK_lock_device() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->lock_device(); + }); + } + + NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_password_safe_slot_name(slot_number); + }); + } + + NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_password_safe_slot_login(slot_number); + }); + } + NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_password_safe_slot_password(slot_number); + }); + } + NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login, + const char *slot_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->write_password_safe_slot(slot_number, slot_name, slot_login, slot_password); + }); + } + + NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->erase_password_safe_slot(slot_number); + }); + } + + NK_C_API int NK_is_AES_supported(const char *user_password) { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return (uint8_t)m->is_AES_supported(user_password); + }); + } + + NK_C_API int NK_login_auto() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return (uint8_t)m->connect(); + }); + } + + // storage commands + + NK_C_API int NK_send_startup(uint64_t seconds_from_epoch) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->send_startup(seconds_from_epoch); + }); + } + + NK_C_API int NK_unlock_encrypted_volume(const char* user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->unlock_encrypted_volume(user_pin); + }); + } + + NK_C_API int NK_lock_encrypted_volume() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->lock_encrypted_volume(); + }); + } + + NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->unlock_hidden_volume(hidden_volume_password); + }); + } + + NK_C_API int NK_lock_hidden_volume() { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->lock_hidden_volume(); + }); + } + + NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, + const char *hidden_volume_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->create_hidden_volume(slot_nr, start_percent, end_percent, + hidden_volume_password); + }); + } + + NK_C_API int NK_set_unencrypted_read_only(const char* user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_unencrypted_read_only(user_pin); + }); + } + + NK_C_API int NK_set_unencrypted_read_write(const char* user_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->set_unencrypted_read_write(user_pin); + }); + } + + NK_C_API int NK_export_firmware(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->export_firmware(admin_pin); + }); + } + + NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->clear_new_sd_card_warning(admin_pin); + }); + } + + NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->fill_SD_card_with_random_data(admin_pin); + }); + } + + NK_C_API int NK_change_update_password(const char* current_update_password, + const char* new_update_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_update_password(current_update_password, new_update_password); + }); + } + + NK_C_API const char* NK_get_status_storage_as_string() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_status_storage_as_string(); + }); + } + + NK_C_API const char* NK_get_SD_usage_data_as_string() { + auto m = NitrokeyManager::instance(); + return get_with_string_result([&]() { + return m->get_SD_usage_data_as_string(); + }); + } + + NK_C_API int NK_get_progress_bar_value() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_progress_bar_value(); + }); + } + + NK_C_API int NK_get_major_firmware_version() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_minor_firmware_version(); + }); + } + + +#ifdef __cplusplus +} +#endif @@ -3,468 +3,474 @@ #include <stdint.h> -#define NK_C_API - -extern "C" -{ -/** - * Set debug level of messages written on stderr - * @param state state=True - all messages, state=False - only errors level - */ -NK_C_API void NK_set_debug(bool state); - -/** - * Connect to device of given model. Currently library can be connected only to one device at once. - * @param device_model char 'S': Nitrokey Storage, 'P': Nitrokey Pro - * @return 1 if connected, 0 if wrong model or cannot connect - */ -NK_C_API int NK_login(const char *device_model); - -/** - * Connect to first available device, starting checking from Pro 1st to Storage 2nd. - * @return 1 if connected, 0 if wrong model or cannot connect - */ -NK_C_API int NK_login_auto(); - -/** - * Disconnect from the device. - * @return command processing error code - */ -NK_C_API int NK_logout(); - -/** - * Return the debug status string. Debug purposes. - * @return command processing error code - */ -NK_C_API const char * NK_status(); - -/** - * Return the device's serial number string in hex. - * @return string device's serial number in hex - */ -NK_C_API const char * NK_device_serial_number(); - -/** - * Get last command processing status. Useful for commands which returns the results of their own and could not return - * an error code. - * @return previous command processing error code - */ -NK_C_API uint8_t NK_get_last_command_status(); - -/** - * Lock device - cancel any user device unlocking. - * @return command processing error code - */ -NK_C_API int NK_lock_device(); - -/** - * Authenticates the user on USER privilages with user_password and sets user's temporary password on device to user_temporary_password. - * @param user_password char[25](Pro) current user password - * @param user_temporary_password char[25](Pro) user temporary password to be set on device for further communication (authentication command) - * @return command processing error code - */ -NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password); - -/** - * Authenticates the user on ADMIN privilages with admin_password and sets user's temporary password on device to admin_temporary_password. - * @param admin_password char[25](Pro) current administrator PIN - * @param admin_temporary_password char[25](Pro) admin temporary password to be set on device for further communication (authentication command) - * @return command processing error code - */ -NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password); - -/** - * Execute a factory reset. - * @param admin_password char[20](Pro) current administrator PIN - * @return command processing error code - */ -NK_C_API int NK_factory_reset(const char* admin_password); - -/** - * Generates AES key on the device - * @param admin_password char[20](Pro) current administrator PIN - * @return command processing error code - */ -NK_C_API int NK_build_aes_key(const char* admin_password); - -/** - * Unlock user PIN locked after 3 incorrect codes tries. - * @param admin_password char[20](Pro) current administrator PIN - * @return command processing error code - */ -NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password); - -/** - * Write general config to the device - * @param numlock set value in range [0-1] to send HOTP code from slot 'numlock' after double pressing numlock - * or outside the range to disable this function - * @param capslock similar to numlock but with capslock - * @param scrolllock similar to numlock but with scrolllock - * @param enable_user_password set True to enable OTP PIN protection (request PIN each OTP code request) - * @param delete_user_password set True to disable OTP PIN protection (request PIN each OTP code request) - * @param admin_temporary_password current admin temporary password - * @return command processing error code - */ -NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, - bool enable_user_password, bool delete_user_password, const char *admin_temporary_password); - -/** - * Get currently set config - status of function Numlock/Capslock/Scrollock OTP sending and is enabled PIN protected OTP - * @see NK_write_config - * @return uint8_t general_config[5]: - * uint8_t numlock; - uint8_t capslock; - uint8_t scrolllock; - uint8_t enable_user_password; - uint8_t delete_user_password; - - */ -NK_C_API uint8_t* NK_read_config(); - -//OTP - -/** - * Get name of given TOTP slot - * @param slot_number TOTP slot number, slot_number<15 - * @return char[20](Pro) the name of the slot - */ -NK_C_API const char * NK_get_totp_slot_name(uint8_t slot_number); - -/** - * - * @param slot_number HOTP slot number, slot_number<3 - * @return char[20](Pro) the name of the slot - */ -NK_C_API const char * NK_get_hotp_slot_name(uint8_t slot_number); - -/** - * Erase HOTP slot data from the device - * @param slot_number HOTP slot number, slot_number<3 - * @param temporary_password admin temporary password - * @return command processing error code - */ -NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password); - -/** - * Erase TOTP slot data from the device - * @param slot_number TOTP slot number, slot_number<15 - * @param temporary_password admin temporary password - * @return command processing error code - */ -NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password); - -/** - * Write HOTP slot data to the device - * @param slot_number HOTP slot number, slot_number<3 - * @param slot_name char[15](Pro) desired slot name - * @param secret char[20](Pro) 160-bit secret - * @param hotp_counter uint32_t starting value of HOTP counter - * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) - * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock - * @param use_tokenID @see token_ID - * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section - * @param temporary_password char[25](Pro) admin temporary password - * @return command processing error code - */ -NK_C_API int NK_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); - -/** - * Write TOTP slot data to the device - * @param slot_number TOTP slot number, slot_number<15 - * @param slot_name char[15](Pro) desired slot name - * @param secret char[20](Pro) 160-bit secret - * @param time_window uint16_t time window for this TOTP - * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) - * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock - * @param use_tokenID @see token_ID - * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section - * @param temporary_password char[20](Pro) admin temporary password - * @return command processing error code - */ -NK_C_API int NK_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); - -/** - * Get HOTP code from the device - * @param slot_number HOTP slot number, slot_number<3 - * @return HOTP code - */ -NK_C_API const char * NK_get_hotp_code(uint8_t slot_number); - -/** - * Get HOTP code from the device (PIN protected) - * @param slot_number HOTP slot number, slot_number<3 - * @param user_temporary_password char[25](Pro) user temporary password if PIN protected OTP codes are enabled, - * otherwise should be set to empty string - '' - * @return HOTP code - */ -NK_C_API const char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password); - -/** - * Get TOTP code from the device - * @param slot_number TOTP slot number, slot_number<15 - * @param challenge TOTP challenge - * @param last_totp_time last time - * @param last_interval last interval - * @return TOTP code - */ -NK_C_API const char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, - uint8_t last_interval); - -/** - * Get TOTP code from the device (PIN protected) - * @param slot_number TOTP slot number, slot_number<15 - * @param challenge TOTP challenge - * @param last_totp_time last time - * @param last_interval last interval - * @param user_temporary_password char[25](Pro) user temporary password if PIN protected OTP codes are enabled, - * otherwise should be set to empty string - '' - * @return TOTP code - */ -NK_C_API const char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, - uint64_t last_totp_time, uint8_t last_interval, - const char *user_temporary_password); - -/** - * Set time on the device (for TOTP requests) - * @param time seconds in unix epoch (from 01.01.1970) - * @return command processing error code - */ -NK_C_API int NK_totp_set_time(uint64_t time); - -NK_C_API int NK_totp_get_time(); -//passwords -/** - * Change administrator PIN - * @param current_PIN char[25](Pro) current PIN - * @param new_PIN char[25](Pro) new PIN - * @return command processing error code - */ -NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN); - -/** - * Change user PIN - * @param current_PIN char[25](Pro) current PIN - * @param new_PIN char[25](Pro) new PIN - * @return command processing error code -*/ -NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN); - - -/** - * Get retry count of user PIN - * @return user PIN retry count - */ -NK_C_API uint8_t NK_get_user_retry_count(); - -/** - * Get retry count of admin PIN - * @return admin PIN retry count - */ -NK_C_API uint8_t NK_get_admin_retry_count(); -//password safe - -/** - * Enable password safe access - * @param user_pin char[30](Pro) current user PIN - * @return command processing error code - */ -NK_C_API int NK_enable_password_safe(const char *user_pin); - -/** - * Get password safe slots' status - * @return uint8_t[16] slot statuses - each byte represents one slot with 0 (not programmed) and 1 (programmed) - */ -NK_C_API uint8_t * NK_get_password_safe_slot_status(); - -/** - * Get password safe slot name - * @param slot_number password safe slot number, slot_number<16 - * @return slot name - */ -NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number); - -/** - * Get password safe slot login - * @param slot_number password safe slot number, slot_number<16 - * @return login from the PWS slot - */ -NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number); - -/** - * Get the password safe slot password - * @param slot_number password safe slot number, slot_number<16 - * @return password from the PWS slot - */ -NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number); - -/** - * Write password safe data to the slot - * @param slot_number password safe slot number, slot_number<16 - * @param slot_name char[11](Pro) name of the slot - * @param slot_login char[32](Pro) login string - * @param slot_password char[20](Pro) password string - * @return command processing error code - */ -NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, - const char *slot_login, const char *slot_password); - -/** - * Erase the password safe slot from the device - * @param slot_number password safe slot number, slot_number<16 - * @return command processing error code - */ -NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number); - -/** - * Check whether AES is supported by the device - * @return 0 for no and 1 for yes - */ -NK_C_API int NK_is_AES_supported(const char *user_password); - -/** - * Get device's major firmware version - * @return 7,8 for Pro and major for Storage - */ -NK_C_API int NK_get_major_firmware_version(); - - - -/** - * This command is typically run to initiate - * communication with the device (altough not required). - * It sets time on device and returns its current status - * - a combination of set_time and get_status_storage commands - * Storage only - * @param seconds_from_epoch date and time expressed in seconds - */ -NK_C_API int NK_send_startup(uint64_t seconds_from_epoch); - -/** - * Unlock encrypted volume. - * Storage only - * @param user_pin user pin 20 characters - * @return command processing error code - */ -NK_C_API int NK_unlock_encrypted_volume(const char* user_pin); - -/** - * Locks encrypted volume - * @return command processing error code - */ -NK_C_API int NK_lock_encrypted_volume(); - -/** - * Unlock hidden volume and lock encrypted volume. - * Requires encrypted volume to be unlocked. - * Storage only - * @param hidden_volume_password 20 characters - * @return command processing error code - */ -NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password); - -/** - * Locks hidden volume - * @return command processing error code - */ -NK_C_API int NK_lock_hidden_volume(); - -/** - * Create hidden volume. - * Requires encrypted volume to be unlocked. - * Storage only - * @param slot_nr slot number in range 0-3 - * @param start_percent volume begin expressed in percent of total available storage, int in range 0-99 - * @param end_percent volume end expressed in percent of total available storage, int in range 1-100 - * @param hidden_volume_password 20 characters - * @return command processing error code - */ -NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, - const char *hidden_volume_password); - -/** - * Make unencrypted volume read-only. - * Device hides unencrypted volume for a second therefore make sure - * buffers are flushed before running. - * Storage only - * @param user_pin 20 characters - * @return command processing error code - */ -NK_C_API int NK_set_unencrypted_read_only(const char* user_pin); - -/** - * Make unencrypted volume read-write. - * Device hides unencrypted volume for a second therefore make sure - * buffers are flushed before running. - * Storage only - * @param user_pin 20 characters - * @return command processing error code - */ -NK_C_API int NK_set_unencrypted_read_write(const char* user_pin); - -/** - * Exports device's firmware to unencrypted volume. - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ -NK_C_API int NK_export_firmware(const char* admin_pin) ; - -/** - * Clear new SD card notification. It is set after factory reset. - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ -NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) ; - -/** - * Fill SD card with random data. - * Should be done on first stick initialization after creating keys. - * Storage only - * @param admin_pin 20 characters - * @return command processing error code - */ -NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) ; - -/** - * Change update password. - * Update password is used for entering update mode, where firmware - * could be uploaded using dfu-programmer or other means. - * Storage only - * @param current_update_password 20 characters - * @param new_update_password 20 characters - * @return command processing error code - */ -NK_C_API int NK_change_update_password(const char* current_update_password, - const char* new_update_password); - -/** - * Get Storage stick status as string. - * Storage only - * @return string with devices attributes - */ -NK_C_API const char* NK_get_status_storage_as_string(); - -/** - * Get SD card usage attributes as string. - * Usable during hidden volumes creation. - * Storage only - * @return string with SD card usage attributes - */ -NK_C_API const char* NK_get_SD_usage_data_as_string(); - -/** - * Get progress value of current long operation. - * Storage only - * @return int in range 0-100 or -1 if device is not busy - */ -NK_C_API int NK_get_progress_bar_value(); - +#ifdef _MSC_VER +#define NK_C_API __declspec(dllexport) +#else +#define NK_C_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + /** + * Set debug level of messages written on stderr + * @param state state=True - all messages, state=False - only errors level + */ + NK_C_API void NK_set_debug(bool state); + + /** + * Connect to device of given model. Currently library can be connected only to one device at once. + * @param device_model char 'S': Nitrokey Storage, 'P': Nitrokey Pro + * @return 1 if connected, 0 if wrong model or cannot connect + */ + NK_C_API int NK_login(const char *device_model); + + /** + * Connect to first available device, starting checking from Pro 1st to Storage 2nd. + * @return 1 if connected, 0 if wrong model or cannot connect + */ + NK_C_API int NK_login_auto(); + + /** + * Disconnect from the device. + * @return command processing error code + */ + NK_C_API int NK_logout(); + + /** + * Return the debug status string. Debug purposes. + * @return command processing error code + */ + NK_C_API const char * NK_status(); + + /** + * Return the device's serial number string in hex. + * @return string device's serial number in hex + */ + NK_C_API const char * NK_device_serial_number(); + + /** + * Get last command processing status. Useful for commands which returns the results of their own and could not return + * an error code. + * @return previous command processing error code + */ + NK_C_API uint8_t NK_get_last_command_status(); + + /** + * Lock device - cancel any user device unlocking. + * @return command processing error code + */ + NK_C_API int NK_lock_device(); + + /** + * Authenticates the user on USER privilages with user_password and sets user's temporary password on device to user_temporary_password. + * @param user_password char[25](Pro) current user password + * @param user_temporary_password char[25](Pro) user temporary password to be set on device for further communication (authentication command) + * @return command processing error code + */ + NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password); + + /** + * Authenticates the user on ADMIN privilages with admin_password and sets user's temporary password on device to admin_temporary_password. + * @param admin_password char[25](Pro) current administrator PIN + * @param admin_temporary_password char[25](Pro) admin temporary password to be set on device for further communication (authentication command) + * @return command processing error code + */ + NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password); + + /** + * Execute a factory reset. + * @param admin_password char[20](Pro) current administrator PIN + * @return command processing error code + */ + NK_C_API int NK_factory_reset(const char* admin_password); + + /** + * Generates AES key on the device + * @param admin_password char[20](Pro) current administrator PIN + * @return command processing error code + */ + NK_C_API int NK_build_aes_key(const char* admin_password); + + /** + * Unlock user PIN locked after 3 incorrect codes tries. + * @param admin_password char[20](Pro) current administrator PIN + * @return command processing error code + */ + NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password); + + /** + * Write general config to the device + * @param numlock set value in range [0-1] to send HOTP code from slot 'numlock' after double pressing numlock + * or outside the range to disable this function + * @param capslock similar to numlock but with capslock + * @param scrolllock similar to numlock but with scrolllock + * @param enable_user_password set True to enable OTP PIN protection (request PIN each OTP code request) + * @param delete_user_password set True to disable OTP PIN protection (request PIN each OTP code request) + * @param admin_temporary_password current admin temporary password + * @return command processing error code + */ + NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, + bool enable_user_password, bool delete_user_password, const char *admin_temporary_password); + + /** + * Get currently set config - status of function Numlock/Capslock/Scrollock OTP sending and is enabled PIN protected OTP + * @see NK_write_config + * @return uint8_t general_config[5]: + * uint8_t numlock; + uint8_t capslock; + uint8_t scrolllock; + uint8_t enable_user_password; + uint8_t delete_user_password; + + */ + NK_C_API uint8_t* NK_read_config(); + + //OTP + + /** + * Get name of given TOTP slot + * @param slot_number TOTP slot number, slot_number<15 + * @return char[20](Pro) the name of the slot + */ + NK_C_API const char * NK_get_totp_slot_name(uint8_t slot_number); + + /** + * + * @param slot_number HOTP slot number, slot_number<3 + * @return char[20](Pro) the name of the slot + */ + NK_C_API const char * NK_get_hotp_slot_name(uint8_t slot_number); + + /** + * Erase HOTP slot data from the device + * @param slot_number HOTP slot number, slot_number<3 + * @param temporary_password admin temporary password + * @return command processing error code + */ + NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password); + + /** + * Erase TOTP slot data from the device + * @param slot_number TOTP slot number, slot_number<15 + * @param temporary_password admin temporary password + * @return command processing error code + */ + NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password); + + /** + * Write HOTP slot data to the device + * @param slot_number HOTP slot number, slot_number<3 + * @param slot_name char[15](Pro) desired slot name + * @param secret char[20](Pro) 160-bit secret + * @param hotp_counter uint32_t starting value of HOTP counter + * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) + * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock + * @param use_tokenID @see token_ID + * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section + * @param temporary_password char[25](Pro) admin temporary password + * @return command processing error code + */ + NK_C_API int NK_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); + + /** + * Write TOTP slot data to the device + * @param slot_number TOTP slot number, slot_number<15 + * @param slot_name char[15](Pro) desired slot name + * @param secret char[20](Pro) 160-bit secret + * @param time_window uint16_t time window for this TOTP + * @param use_8_digits should returned codes be 6 (false) or 8 digits (true) + * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock + * @param use_tokenID @see token_ID + * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section + * @param temporary_password char[20](Pro) admin temporary password + * @return command processing error code + */ + NK_C_API int NK_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); + + /** + * Get HOTP code from the device + * @param slot_number HOTP slot number, slot_number<3 + * @return HOTP code + */ + NK_C_API const char * NK_get_hotp_code(uint8_t slot_number); + + /** + * Get HOTP code from the device (PIN protected) + * @param slot_number HOTP slot number, slot_number<3 + * @param user_temporary_password char[25](Pro) user temporary password if PIN protected OTP codes are enabled, + * otherwise should be set to empty string - '' + * @return HOTP code + */ + NK_C_API const char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password); + + /** + * Get TOTP code from the device + * @param slot_number TOTP slot number, slot_number<15 + * @param challenge TOTP challenge + * @param last_totp_time last time + * @param last_interval last interval + * @return TOTP code + */ + NK_C_API const char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, + uint8_t last_interval); + + /** + * Get TOTP code from the device (PIN protected) + * @param slot_number TOTP slot number, slot_number<15 + * @param challenge TOTP challenge + * @param last_totp_time last time + * @param last_interval last interval + * @param user_temporary_password char[25](Pro) user temporary password if PIN protected OTP codes are enabled, + * otherwise should be set to empty string - '' + * @return TOTP code + */ + NK_C_API const char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, + uint64_t last_totp_time, uint8_t last_interval, + const char *user_temporary_password); + + /** + * Set time on the device (for TOTP requests) + * @param time seconds in unix epoch (from 01.01.1970) + * @return command processing error code + */ + NK_C_API int NK_totp_set_time(uint64_t time); + + NK_C_API int NK_totp_get_time(); + //passwords + /** + * Change administrator PIN + * @param current_PIN char[25](Pro) current PIN + * @param new_PIN char[25](Pro) new PIN + * @return command processing error code + */ + NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN); + + /** + * Change user PIN + * @param current_PIN char[25](Pro) current PIN + * @param new_PIN char[25](Pro) new PIN + * @return command processing error code + */ + NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN); + + + /** + * Get retry count of user PIN + * @return user PIN retry count + */ + NK_C_API uint8_t NK_get_user_retry_count(); + + /** + * Get retry count of admin PIN + * @return admin PIN retry count + */ + NK_C_API uint8_t NK_get_admin_retry_count(); + //password safe + + /** + * Enable password safe access + * @param user_pin char[30](Pro) current user PIN + * @return command processing error code + */ + NK_C_API int NK_enable_password_safe(const char *user_pin); + + /** + * Get password safe slots' status + * @return uint8_t[16] slot statuses - each byte represents one slot with 0 (not programmed) and 1 (programmed) + */ + NK_C_API uint8_t * NK_get_password_safe_slot_status(); + + /** + * Get password safe slot name + * @param slot_number password safe slot number, slot_number<16 + * @return slot name + */ + NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number); + + /** + * Get password safe slot login + * @param slot_number password safe slot number, slot_number<16 + * @return login from the PWS slot + */ + NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number); + + /** + * Get the password safe slot password + * @param slot_number password safe slot number, slot_number<16 + * @return password from the PWS slot + */ + NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number); + + /** + * Write password safe data to the slot + * @param slot_number password safe slot number, slot_number<16 + * @param slot_name char[11](Pro) name of the slot + * @param slot_login char[32](Pro) login string + * @param slot_password char[20](Pro) password string + * @return command processing error code + */ + NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, + const char *slot_login, const char *slot_password); + + /** + * Erase the password safe slot from the device + * @param slot_number password safe slot number, slot_number<16 + * @return command processing error code + */ + NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number); + + /** + * Check whether AES is supported by the device + * @return 0 for no and 1 for yes + */ + NK_C_API int NK_is_AES_supported(const char *user_password); + + /** + * Get device's major firmware version + * @return 7,8 for Pro and major for Storage + */ + NK_C_API int NK_get_major_firmware_version(); + + + + /** + * This command is typically run to initiate + * communication with the device (altough not required). + * It sets time on device and returns its current status + * - a combination of set_time and get_status_storage commands + * Storage only + * @param seconds_from_epoch date and time expressed in seconds + */ + NK_C_API int NK_send_startup(uint64_t seconds_from_epoch); + + /** + * Unlock encrypted volume. + * Storage only + * @param user_pin user pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_unlock_encrypted_volume(const char* user_pin); + + /** + * Locks encrypted volume + * @return command processing error code + */ + NK_C_API int NK_lock_encrypted_volume(); + + /** + * Unlock hidden volume and lock encrypted volume. + * Requires encrypted volume to be unlocked. + * Storage only + * @param hidden_volume_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password); + + /** + * Locks hidden volume + * @return command processing error code + */ + NK_C_API int NK_lock_hidden_volume(); + + /** + * Create hidden volume. + * Requires encrypted volume to be unlocked. + * Storage only + * @param slot_nr slot number in range 0-3 + * @param start_percent volume begin expressed in percent of total available storage, int in range 0-99 + * @param end_percent volume end expressed in percent of total available storage, int in range 1-100 + * @param hidden_volume_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent, + const char *hidden_volume_password); + + /** + * Make unencrypted volume read-only. + * Device hides unencrypted volume for a second therefore make sure + * buffers are flushed before running. + * Storage only + * @param user_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_set_unencrypted_read_only(const char* user_pin); + + /** + * Make unencrypted volume read-write. + * Device hides unencrypted volume for a second therefore make sure + * buffers are flushed before running. + * Storage only + * @param user_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_set_unencrypted_read_write(const char* user_pin); + + /** + * Exports device's firmware to unencrypted volume. + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_export_firmware(const char* admin_pin); + + /** + * Clear new SD card notification. It is set after factory reset. + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin); + + /** + * Fill SD card with random data. + * Should be done on first stick initialization after creating keys. + * Storage only + * @param admin_pin 20 characters + * @return command processing error code + */ + NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin); + + /** + * Change update password. + * Update password is used for entering update mode, where firmware + * could be uploaded using dfu-programmer or other means. + * Storage only + * @param current_update_password 20 characters + * @param new_update_password 20 characters + * @return command processing error code + */ + NK_C_API int NK_change_update_password(const char* current_update_password, + const char* new_update_password); + + /** + * Get Storage stick status as string. + * Storage only + * @return string with devices attributes + */ + NK_C_API const char* NK_get_status_storage_as_string(); + + /** + * Get SD card usage attributes as string. + * Usable during hidden volumes creation. + * Storage only + * @return string with SD card usage attributes + */ + NK_C_API const char* NK_get_SD_usage_data_as_string(); + + /** + * Get progress value of current long operation. + * Storage only + * @return int in range 0-100 or -1 if device is not busy + */ + NK_C_API int NK_get_progress_bar_value(); + +#ifdef __cplusplus } - +#endif #endif //LIBNITROKEY_NK_C_API_H diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 9a9d106..0d702eb 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -10,11 +10,14 @@ #include "include/cxx_semantics.h" #include <functional> +std::mutex nitrokey::proto::send_receive_mtx; + namespace nitrokey{ std::mutex mex_dev_com_manager; -#ifdef __WIN32 +#ifndef strndup +#ifdef _WIN32 #pragma message "Using own strndup" char * strndup(const char* str, size_t maxlen){ size_t len = strnlen(str, maxlen); @@ -24,7 +27,7 @@ char * strndup(const char* str, size_t maxlen){ return dup; } #endif - +#endif using nitrokey::misc::strcpyT; @@ -29,7 +29,19 @@ Following libraries are needed to use libnitrokey on Linux (names of the package ## Compilation -libnitrokey uses CMake as its build system. To compile please run following sequence of commands: +libnitrokey uses CMake as its main build system. As a secondary option it offers building through Qt's qMake. +### Qt +A .pro project file is provided for direct compilation and for inclusion to other projects. + +### Windows MS Visual Studio 2017 +Lately Visual Studio has started handling CMake files directly. After opening the project's directory it should recognize it and initialize build system. Afterwards please run: +1. `CMake -> Cache -> View Cache CMakeLists.txt -> CMakeLists.txt` to edit settings +2. `CMake -> Build All` to build + +It is possible too to use CMake GUI directly with its settings editor. + +### Linux CLI +To compile please run following sequence of commands: ```bash # assuming current dir is ./libnitrokey/ mkdir -p build @@ -50,7 +62,7 @@ Other build options (all take either `ON` or `OFF`): -# Using with Python +# Using libnitrokey with Python To use libnitrokey with Python a [CFFI](http://cffi.readthedocs.io/en/latest/overview.html) library is required (either 2.7+ or 3.0+). It can be installed with: ```bash pip install --user cffi # for python 2.x @@ -137,7 +149,7 @@ Please check NK_C_API.h (C API) for high level commands and include/NitrokeyMana Warning! Before you run unittests please either change both your Admin and User PINs on your Nitrostick to defaults (`12345678` and `123456` respectively) or change the values in tests source code. If you do not change them the tests might lock your device. If it's too late, you can always reset your Nitrokey using instructions from [homepage](https://www.nitrokey.com/de/documentation/how-reset-nitrokey). ## Python tests -Libnitrokey has a great suite of tests written in Python under the path: `unittest/test_*.py`: +Libnitrokey has a great suite of tests written in Python 3 under the path: `unittest/test_*.py`: * `test_pro.py` - contains tests of OTP, Password Safe and PIN control functionality. Could be run on both Pro and Storage devices. * `test_storage.py` - contains tests of Encrypted Volumes functionality. Could be run only on Storage. The tests themselves show how to handle common requests to device. @@ -27,7 +27,7 @@ Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, m_vid(vid), m_pid(pid), m_model(model), - m_retry_sending_count(3), + m_retry_sending_count(1), m_retry_receiving_count(retry_receiving_count), m_retry_timeout(retry_timeout), m_send_receive_delay(send_receive_delay), @@ -93,7 +93,7 @@ int Device::send(const void *packet) { mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE); if (send_feature_report < 0) _reconnect(); //add thread sleep? - LOG(std::string("Sending attempt: ")+std::to_string(i) + " / 3" , Loglevel::DEBUG_L2); + LOG(std::string("Sending attempt: ")+std::to_string(i+1) + " / 3" , Loglevel::DEBUG_L2); } return send_feature_report; } @@ -200,7 +200,7 @@ Stick10::Stick10(): Stick20::Stick20(): - Device(0x20a0, 0x4109, DeviceModel::STORAGE, 20ms, 20, 20ms) + Device(0x20a0, 0x4109, DeviceModel::STORAGE, 20ms, 10, 20ms) { setDefaultDelay(); } diff --git a/hidapi b/hidapi -Subproject 324dc7c0d125f57a06e1107e90e49eb4377bd03 +Subproject b767b43f3e6f9c5b92ea7d738331deb8e03c4ba diff --git a/include/command.h b/include/command.h index 3f711c0..279754a 100644 --- a/include/command.h +++ b/include/command.h @@ -11,7 +11,7 @@ #define print_to_ss_volatile(x) ( ss << " " << (#x) <<":\t" << "***********" << std::endl ); #endif #define hexdump_to_ss(x) (ss << #x":\n"\ - << ::nitrokey::misc::hexdump((const char *) (&x), sizeof x, false)); + << ::nitrokey::misc::hexdump((const uint8_t *) (&x), sizeof x, false)); namespace nitrokey { namespace proto { diff --git a/include/device_proto.h b/include/device_proto.h index b557384..bb14b42 100644 --- a/include/device_proto.h +++ b/include/device_proto.h @@ -37,6 +37,9 @@ namespace nitrokey { namespace proto { + extern std::mutex send_receive_mtx; + + /* * POD types for HID proto commands * Instances are meant to be __packed. @@ -215,7 +218,6 @@ namespace nitrokey { using namespace ::nitrokey::log; using namespace std::chrono_literals; - static std::mutex send_receive_mtx; std::lock_guard<std::mutex> guard(send_receive_mtx); LOG(__FUNCTION__, Loglevel::DEBUG_L2); @@ -306,12 +308,13 @@ namespace nitrokey { } if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy)) { dev->m_counters.busy++; - if (busy_counter++<10) { + if (busy_counter++<3) { receiving_retry_counter++; LOG("Status busy, not decreasing receiving_retry_counter counter: " + std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2); } else { retry_timeout *= 2; + retry_timeout = std::min(retry_timeout, 300ms); busy_counter = 0; LOG("Status busy, decreasing receiving_retry_counter counter: " + std::to_string(receiving_retry_counter) + ", current delay:" @@ -352,12 +355,6 @@ namespace nitrokey { clear_packet(outp); - if (!resp.isCRCcorrect()) - LOGD(std::string("Accepting response from device with invalid CRC. ") - + "Command ID: " + std::to_string(resp.command_id) + " " + - commandid_to_string(static_cast<CommandID>(resp.command_id)) - ); - if (status <= 0) { dev->m_counters.receiving_error++; @@ -398,6 +395,14 @@ namespace nitrokey { dev->m_counters.successful_storage_commands++; } + if (!resp.isCRCcorrect()) + LOG(std::string("Accepting response from device with invalid CRC. ") + + "Command ID: " + std::to_string(resp.command_id) + " " + + commandid_to_string(static_cast<CommandID>(resp.command_id)) + " " + + "Reported and calculated: " + std::to_string(resp.crc) + "!=" + std::to_string(resp.calculate_CRC()), + Loglevel::WARNING + ); + // See: DeviceResponse return resp; } diff --git a/include/dissect.h b/include/dissect.h index 69a5129..06b99fa 100644 --- a/include/dissect.h +++ b/include/dissect.h @@ -22,7 +22,7 @@ class QueryDissector : semantics::non_constructible { #ifdef LOG_VOLATILE_DATA out << "Raw HID packet:" << std::endl; - out << ::nitrokey::misc::hexdump((const char *)(&pod), sizeof pod); + out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod); #endif out << "Contents:" << std::endl; @@ -87,7 +87,7 @@ class ResponseDissector : semantics::non_constructible { #ifdef LOG_VOLATILE_DATA out << "Raw HID packet:" << std::endl; - out << ::nitrokey::misc::hexdump((const char *)(&pod), sizeof pod); + out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod); #endif out << "Device status:\t" << pod.device_status + 0 << " " diff --git a/include/misc.h b/include/misc.h index 176b77c..25f3107 100644 --- a/include/misc.h +++ b/include/misc.h @@ -62,7 +62,7 @@ typename T::CommandPayload get_payload(){ CMDTYPE::CommandTransaction::run(stick, p); } - std::string hexdump(const char *p, size_t size, bool print_header=true, bool print_ascii=true, + std::string hexdump(const uint8_t *p, size_t size, bool print_header=true, bool print_ascii=true, bool print_empty=true); uint32_t stm_crc32(const uint8_t *data, size_t size); std::vector<uint8_t> hex_string_to_byte(const char* hexString); diff --git a/include/stick10_commands.h b/include/stick10_commands.h index e863328..8f3ceef 100644 --- a/include/stick10_commands.h +++ b/include/stick10_commands.h @@ -130,7 +130,7 @@ class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> { ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; ss << std::endl; ss << "slot_counter:\t[" << (int)slot_counter << "]\t" - << ::nitrokey::misc::hexdump((const char *)(&slot_counter), sizeof slot_counter, false); + << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false); return ss.str(); } @@ -334,7 +334,7 @@ class ReadSlot : Command<CommandID::READ_SLOT> { ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ; ss << std::endl; ss << "slot_counter:\t[" << (int)slot_counter << "]\t" - << ::nitrokey::misc::hexdump((const char *)(&slot_counter), sizeof slot_counter, false); + << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false); return ss.str(); } } __packed; @@ -372,13 +372,13 @@ class GetStatus : Command<CommandID::GET_STATUS> { ss << "firmware_version:\t" << "[" << firmware_version << "]" << "\t" << ::nitrokey::misc::hexdump( - (const char *)(&firmware_version), sizeof firmware_version, false); + (const uint8_t *)(&firmware_version), sizeof firmware_version, false); ss << "card_serial_u32:\t" << std::hex << card_serial_u32 << std::endl; ss << "card_serial:\t" - << ::nitrokey::misc::hexdump((const char *)(card_serial), + << ::nitrokey::misc::hexdump((const uint8_t *)(card_serial), sizeof card_serial, false); ss << "general_config:\t" - << ::nitrokey::misc::hexdump((const char *)(general_config), + << ::nitrokey::misc::hexdump((const uint8_t *)(general_config), sizeof general_config, false); ss << "numlock:\t" << (int)numlock << std::endl; ss << "capslock:\t" << (int)capslock << std::endl; diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index 4209380..361682d 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -94,7 +94,7 @@ namespace nitrokey { ss << "id:\t" << (int)id << std::endl; #ifdef LOG_VOLATILE_DATA ss << "data:" << std::endl - << ::nitrokey::misc::hexdump((const char *) (&data), sizeof data); + << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data); #else ss << " Volatile data not logged" << std::endl; #endif @@ -113,7 +113,7 @@ namespace nitrokey { std::stringstream ss; #ifdef LOG_VOLATILE_DATA ss << "data:" << std::endl - << ::nitrokey::misc::hexdump((const char *) (&data), sizeof data); + << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data); #else ss << " Volatile data not logged" << std::endl; #endif @@ -165,7 +165,7 @@ namespace nitrokey { ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; ss << "slot_number:\t" << (int) (slot_number) << std::endl; ss << "slot_counter_or_interval:\t[" << (int) slot_counter_or_interval << "]\t" - << ::nitrokey::misc::hexdump((const char *) (&slot_counter_or_interval), sizeof slot_counter_or_interval, false); + << ::nitrokey::misc::hexdump((const uint8_t *) (&slot_counter_or_interval), sizeof slot_counter_or_interval, false); ss << "slot_token_id:\t"; for (auto i : slot_token_id) diff --git a/include/stick20_commands.h b/include/stick20_commands.h index 5f99d28..eb58af7 100644 --- a/include/stick20_commands.h +++ b/include/stick20_commands.h @@ -111,7 +111,7 @@ namespace nitrokey { std::string dissect() const { std::stringstream ss; ss << "_padding:" << std::endl - << ::nitrokey::misc::hexdump((const char *) (_padding), + << ::nitrokey::misc::hexdump((const uint8_t *) (_padding), sizeof _padding); print_to_ss((int) SendCounter_u8); print_to_ss((int) SendDataType_u8); @@ -138,9 +138,9 @@ namespace nitrokey { union{ uint8_t VersionInfo_au8[4]; struct { - uint8_t __reserved; + uint8_t _reserved; uint8_t minor; - uint8_t __reserved2; + uint8_t _reserved2; uint8_t major; } __packed versionInfo; } __packed; @@ -184,14 +184,19 @@ namespace nitrokey { print_to_ss( MagicNumber_StickConfig_u16 ); print_to_ss((int) ReadWriteFlagUncryptedVolume_u8 ); print_to_ss((int) ReadWriteFlagCryptedVolume_u8 ); + print_to_ss((int) ReadWriteFlagHiddenVolume_u8 ); print_to_ss((int) VersionInfo_au8[1] ); print_to_ss((int) VersionInfo_au8[3] ); - print_to_ss((int) ReadWriteFlagHiddenVolume_u8 ); print_to_ss((int) FirmwareLocked_u8 ); print_to_ss((int) NewSDCardFound_u8 ); + print_to_ss((int) NewSDCardFound_st.NewCard ); + print_to_ss((int) NewSDCardFound_st.Counter ); print_to_ss((int) SDFillWithRandomChars_u8 ); print_to_ss( ActiveSD_CardID_u32 ); print_to_ss((int) VolumeActiceFlag_u8 ); + print_to_ss((int) VolumeActiceFlag_st.unencrypted ); + print_to_ss((int) VolumeActiceFlag_st.encrypted ); + print_to_ss((int) VolumeActiceFlag_st.hidden); print_to_ss((int) NewSmartCardFound_u8 ); print_to_ss((int) UserPwRetryCount ); print_to_ss((int) AdminPwRetryCount ); diff --git a/libnitrokey.pro b/libnitrokey.pro new file mode 100644 index 0000000..f7711bf --- /dev/null +++ b/libnitrokey.pro @@ -0,0 +1,74 @@ +# Created by and for Qt Creator. This file was created for editing the project sources only. +# You may attempt to use it for building too, by modifying this file here. + +CONFIG += c++14 shared debug + +TEMPLATE = lib +TARGET = nitrokey + +HEADERS = \ + $$PWD/hidapi/hidapi/hidapi.h \ + $$PWD/include/command.h \ + $$PWD/include/command_id.h \ + $$PWD/include/CommandFailedException.h \ + $$PWD/include/cxx_semantics.h \ + $$PWD/include/device.h \ + $$PWD/include/device_proto.h \ + $$PWD/include/DeviceCommunicationExceptions.h \ + $$PWD/include/dissect.h \ + $$PWD/include/inttypes.h \ + $$PWD/include/LibraryException.h \ + $$PWD/include/log.h \ + $$PWD/include/LongOperationInProgressException.h \ + $$PWD/include/misc.h \ + $$PWD/include/NitrokeyManager.h \ + $$PWD/include/stick10_commands.h \ + $$PWD/include/stick10_commands_0.8.h \ + $$PWD/include/stick20_commands.h \ + $$PWD/NK_C_API.h + +SOURCES = \ + $$PWD/command_id.cc \ + $$PWD/device.cc \ + $$PWD/DeviceCommunicationExceptions.cpp \ + $$PWD/log.cc \ + $$PWD/misc.cc \ + $$PWD/NitrokeyManager.cc \ + $$PWD/NK_C_API.cc + + +tests { + SOURCES += \ + $$PWD/unittest/catch_main.cpp \ + $$PWD/unittest/test.cc \ + $$PWD/unittest/test2.cc \ + $$PWD/unittest/test3.cc \ + $$PWD/unittest/test_C_API.cpp \ + $$PWD/unittest/test_HOTP.cc +} + +unix:!macx{ +# SOURCES += $$PWD/hidapi/linux/hid.c + LIBS += -lhidapi-libusb +} + +macx{ + SOURCES += $$PWD/hidapi/mac/hid.c + LIBS+= -framework IOKit -framework CoreFoundation +} + +win32 { + SOURCES += $$PWD/hidapi/windows/hid.c + LIBS += -lsetupapi +} + +INCLUDEPATH = \ + $$PWD/. \ + $$PWD/hidapi/hidapi \ + $$PWD/include \ + $$PWD/include/hidapi \ + $$PWD/unittest \ + $$PWD/unittest/Catch/single_include + +#DEFINES = + @@ -39,20 +39,20 @@ namespace misc { }; #include <cctype> -::std::string hexdump(const char *p, size_t size, bool print_header, +::std::string hexdump(const uint8_t *p, size_t size, bool print_header, bool print_ascii, bool print_empty) { ::std::stringstream out; char formatbuf[128]; - const char *pstart = p; + const uint8_t *pstart = p; - for (const char *pend = p + size; p < pend;) { + for (const uint8_t *pend = p + size; p < pend;) { if (print_header){ snprintf(formatbuf, 128, "%04x\t", static_cast<int> (p - pstart)); out << formatbuf; } - const char* pp = p; - for (const char *le = p + 16; p < le; p++) { + const uint8_t* pp = p; + for (const uint8_t *le = p + 16; p < le; p++) { if (p < pend){ snprintf(formatbuf, 128, "%02x ", uint8_t(*p)); out << formatbuf; @@ -63,8 +63,8 @@ namespace misc { } if(print_ascii){ - out << "\t"; - for (const char *le = pp + 16; pp < le && pp < pend; pp++) { + out << " "; + for (const uint8_t *le = pp + 16; pp < le && pp < pend; pp++) { if (std::isgraph(*pp)) out << uint8_t(*pp); else diff --git a/unittest/conftest.py b/unittest/conftest.py index 67b45aa..04e85ff 100644 --- a/unittest/conftest.py +++ b/unittest/conftest.py @@ -22,9 +22,9 @@ def C(request): a = iter(declarations) for declaration in a: - if declaration.startswith('NK_C_API'): + if declaration.strip().startswith('NK_C_API'): declaration = declaration.replace('NK_C_API', '').strip() - while not ';' in declaration: + while ';' not in declaration: declaration += (next(a)).strip() print(declaration) ffi.cdef(declaration, override=True) @@ -32,11 +32,19 @@ def C(request): C = None import os, sys path_build = os.path.join("..", "build") - paths = [ os.path.join(path_build,"libnitrokey-log.so"), - os.path.join(path_build,"libnitrokey.so")] + paths = [ + os.path.join(path_build,"libnitrokey-log.so"), + os.path.join(path_build,"libnitrokey.so"), + os.path.join(path_build,"libnitrokey-log.dll"), + os.path.join(path_build,"libnitrokey.dll"), + os.path.join(path_build,"nitrokey-log.dll"), + os.path.join(path_build,"nitrokey.dll"), + ] for p in paths: - print p + print(p) + p = os.path.abspath(p) if os.path.exists(p): + print("Found: "+p) C = ffi.dlopen(p) break else: diff --git a/unittest/constants.py b/unittest/constants.py index 0897b42..f3e876a 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -1,22 +1,26 @@ -from enum import Enum from misc import to_hex +def bb(x): + return bytes(x, encoding='ascii') + + RFC_SECRET_HR = '12345678901234567890' RFC_SECRET = to_hex(RFC_SECRET_HR) # '31323334353637383930...' +bbRFC_SECRET = bb(RFC_SECRET) # print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) ) -class DefaultPasswords(Enum): - ADMIN = '12345678' - USER = '123456' - ADMIN_TEMP = '123123123' - USER_TEMP = '234234234' - UPDATE = '12345678' - UPDATE_TEMP = '123update123' +class DefaultPasswords: + ADMIN = b'12345678' + USER = b'123456' + ADMIN_TEMP = b'123123123' + USER_TEMP = b'234234234' + UPDATE = b'12345678' + UPDATE_TEMP = b'123update123' -class DeviceErrorCode(Enum): +class DeviceErrorCode: STATUS_OK = 0 BUSY = 1 # busy or busy progressbar in place of wrong_CRC status NOT_PROGRAMMED = 3 @@ -25,7 +29,7 @@ class DeviceErrorCode(Enum): STATUS_AES_DEC_FAILED = 0xa -class LibraryErrors(Enum): +class LibraryErrors: TOO_LONG_STRING = 200 INVALID_SLOT = 201 INVALID_HEX_STRING = 202 diff --git a/unittest/requirements.txt b/unittest/requirements.txt index 2cb9c05..5c0110b 100644 --- a/unittest/requirements.txt +++ b/unittest/requirements.txt @@ -1,5 +1,4 @@ cffi pytest-repeat pytest-randomly -enum oath
\ No newline at end of file diff --git a/unittest/test_issues.cc b/unittest/test_issues.cc index ec3f933..63ce678 100644 --- a/unittest/test_issues.cc +++ b/unittest/test_issues.cc @@ -15,7 +15,7 @@ using namespace nitrokey; bool test_36(){ auto i = NitrokeyManager::instance(); - i->set_debug(true); + i->set_loglevel(3); REQUIRE(i->connect()); for (int j = 0; j < 200; ++j) { @@ -28,7 +28,7 @@ bool test_36(){ bool test_31(){ auto i = NitrokeyManager::instance(); - i->set_debug(true); + i->set_loglevel(3); REQUIRE(i->connect()); // i->unlock_encrypted_volume(default_user_pin); diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 3f1f0a3..99dcf4d 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -1,7 +1,7 @@ import pytest from conftest import skip_if_device_version_lower_than -from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET +from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bb, bbRFC_SECRET from misc import ffi, gs, wait, cast_pointer_to_tuple from misc import is_pro_rtm_07, is_pro_rtm_08, is_storage @@ -11,15 +11,15 @@ def test_enable_password_safe(C): All Password Safe tests depend on AES keys being initialized. They will fail otherwise. """ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK - assert C.NK_enable_password_safe('wrong_password') == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_enable_password_safe(b'wrong_password') == DeviceErrorCode.WRONG_PASSWORD assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK def test_write_password_safe_slot(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK - assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_NOT_AUTHORIZED + assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_NOT_AUTHORIZED assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_OK + assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK @pytest.mark.slowtest @@ -29,7 +29,7 @@ def test_write_all_password_safe_slots_and_read_10_times(C): numbers = '1234567890'*4 s += numbers[:wid-len(s)] assert len(s) == wid - return s + return bb(s) def get_pass(suffix): return fill('pass' + suffix, 20) @@ -65,7 +65,7 @@ def test_read_all_password_safe_slots_10_times(C): numbers = '1234567890'*4 s += numbers[:wid-len(s)] assert len(s) == wid - return s + return bb(s) def get_pass(suffix): return fill('pass' + suffix, 20) @@ -90,31 +90,31 @@ def test_read_all_password_safe_slots_10_times(C): def test_get_password_safe_slot_name(C): assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_OK + assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK - assert gs(C.NK_get_password_safe_slot_name(0)) == '' + assert gs(C.NK_get_password_safe_slot_name(0)) == b'' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_NOT_AUTHORIZED assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - assert gs(C.NK_get_password_safe_slot_name(0)) == 'slotname1' + assert gs(C.NK_get_password_safe_slot_name(0)) == b'slotname1' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK def test_get_password_safe_slot_login_password(C): assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_OK + assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK slot_login = C.NK_get_password_safe_slot_login(0) assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK - assert gs(slot_login) == 'login1' + assert gs(slot_login) == b'login1' slot_password = gs(C.NK_get_password_safe_slot_password(0)) assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK - assert slot_password == 'pass1' + assert slot_password == b'pass1' def test_erase_password_safe_slot(C): assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_erase_password_safe_slot(0) == DeviceErrorCode.STATUS_OK - assert gs(C.NK_get_password_safe_slot_name(0)) == '' + assert gs(C.NK_get_password_safe_slot_name(0)) == b'' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK # TODO CHECK shouldn't this be DeviceErrorCode.NOT_PROGRAMMED ? @@ -122,7 +122,7 @@ def test_password_safe_slot_status(C): C.NK_set_debug(True) assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_erase_password_safe_slot(0) == DeviceErrorCode.STATUS_OK - assert C.NK_write_password_safe_slot(1, 'slotname2', 'login2', 'pass2') == DeviceErrorCode.STATUS_OK + assert C.NK_write_password_safe_slot(1, b'slotname2', b'login2', b'pass2') == DeviceErrorCode.STATUS_OK safe_slot_status = C.NK_get_password_safe_slot_status() assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK is_slot_programmed = list(ffi.cast("uint8_t [16]", safe_slot_status)[0:16]) @@ -131,6 +131,7 @@ def test_password_safe_slot_status(C): assert is_slot_programmed[1] == 1 +# TODO TOREGISTER def test_issue_device_locks_on_second_key_generation_in_sequence(C): if is_pro_rtm_07(C) or is_pro_rtm_08(C): pytest.skip("issue to register: device locks up " @@ -146,6 +147,7 @@ def test_regenerate_aes_key(C): assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK +@pytest.mark.skip def test_enable_password_safe_after_factory_reset(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK if is_storage(C): @@ -171,13 +173,13 @@ def test_destroy_password_safe(C): C.NK_set_debug(True) assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK # write password safe slot - assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_OK + assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK # read slot - assert gs(C.NK_get_password_safe_slot_name(0)) == 'slotname1' + assert gs(C.NK_get_password_safe_slot_name(0)) == b'slotname1' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK slot_login = C.NK_get_password_safe_slot_login(0) assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK - assert gs(slot_login) == 'login1' + assert gs(slot_login) == b'login1' # destroy password safe by regenerating aes key assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK @@ -186,7 +188,7 @@ def test_destroy_password_safe(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - assert gs(C.NK_get_password_safe_slot_name(0)) != 'slotname1' + assert gs(C.NK_get_password_safe_slot_name(0)) != b'slotname1' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK # check was slot status cleared @@ -199,22 +201,22 @@ def test_destroy_password_safe(C): def test_is_AES_supported(C): if is_storage(C): pytest.skip("Storage does not implement this command") - assert C.NK_is_AES_supported('wrong password') != 1 + assert C.NK_is_AES_supported(b'wrong password') != 1 assert C.NK_get_last_command_status() == DeviceErrorCode.WRONG_PASSWORD assert C.NK_is_AES_supported(DefaultPasswords.USER) == 1 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK def test_admin_PIN_change(C): - new_password = '123123123' - assert C.NK_change_admin_PIN('wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD + new_password = b'123123123' + assert C.NK_change_admin_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_change_admin_PIN(DefaultPasswords.ADMIN, new_password) == DeviceErrorCode.STATUS_OK assert C.NK_change_admin_PIN(new_password, DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK def test_user_PIN_change(C): - new_password = '123123123' - assert C.NK_change_user_PIN('wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD + new_password = b'123123123' + assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_change_user_PIN(DefaultPasswords.USER, new_password) == DeviceErrorCode.STATUS_OK assert C.NK_change_user_PIN(new_password, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK @@ -223,7 +225,7 @@ def test_admin_retry_counts(C): default_admin_retry_count = 3 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_get_admin_retry_count() == default_admin_retry_count - assert C.NK_change_admin_PIN('wrong_password', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_change_admin_PIN(b'wrong_password', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_get_admin_retry_count() == default_admin_retry_count - 1 assert C.NK_change_admin_PIN(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK assert C.NK_get_admin_retry_count() == default_admin_retry_count @@ -231,7 +233,7 @@ def test_admin_retry_counts(C): def test_user_retry_counts_change_PIN(C): assert C.NK_change_user_PIN(DefaultPasswords.USER, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - wrong_password = 'wrong_password' + wrong_password = b'wrong_password' default_user_retry_count = 3 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_get_user_retry_count() == default_user_retry_count @@ -244,7 +246,7 @@ def test_user_retry_counts_PWSafe(C): default_user_retry_count = 3 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_get_user_retry_count() == default_user_retry_count - assert C.NK_enable_password_safe('wrong_password') == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_enable_password_safe(b'wrong_password') == DeviceErrorCode.WRONG_PASSWORD assert C.NK_get_user_retry_count() == default_user_retry_count - 1 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_get_user_retry_count() == default_user_retry_count @@ -254,15 +256,15 @@ def test_unlock_user_password(C): C.NK_set_debug(True) default_user_retry_count = 3 default_admin_retry_count = 3 - new_password = '123123123' + new_password = b'123123123' assert C.NK_get_user_retry_count() == default_user_retry_count - assert C.NK_change_user_PIN('wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD - assert C.NK_change_user_PIN('wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD - assert C.NK_change_user_PIN('wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_get_user_retry_count() == default_user_retry_count - 3 assert C.NK_get_admin_retry_count() == default_admin_retry_count - assert C.NK_unlock_user_password('wrong password', DefaultPasswords.USER) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_unlock_user_password(b'wrong password', DefaultPasswords.USER) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_get_admin_retry_count() == default_admin_retry_count - 1 assert C.NK_unlock_user_password(DefaultPasswords.ADMIN, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_get_user_retry_count() == default_user_retry_count @@ -270,12 +272,12 @@ def test_unlock_user_password(C): def test_admin_auth(C): - assert C.NK_first_authenticate('wrong_password', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_first_authenticate(b'wrong_password', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK def test_user_auth(C): - assert C.NK_user_authenticate('wrong_password', DefaultPasswords.USER_TEMP) == DeviceErrorCode.WRONG_PASSWORD + assert C.NK_user_authenticate(b'wrong_password', DefaultPasswords.USER_TEMP) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK @@ -284,7 +286,7 @@ def check_HOTP_RFC_codes(C, func, prep=None, use_8_digits=False): # https://tools.ietf.org/html/rfc4226#page-32 """ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(1, 'python_test', RFC_SECRET, 0, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(1, b'python_test', bbRFC_SECRET, 0, use_8_digits, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK test_data = [ 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871, @@ -295,7 +297,7 @@ def check_HOTP_RFC_codes(C, func, prep=None, use_8_digits=False): prep() r = func(1) code = str(code)[-8:] if use_8_digits else str(code)[-6:] - assert code == r + assert bb(code) == r @pytest.mark.parametrize("use_8_digits", [False, True, ]) @@ -322,12 +324,12 @@ def test_HOTP_token(C): assert C.NK_write_config(255, 255, 255, use_pin_protection, not use_pin_protection, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - token_ID = "AAV100000022" - assert C.NK_write_hotp_slot(1, 'python_test', RFC_SECRET, 0, False, False, True, token_ID, + token_ID = b"AAV100000022" + assert C.NK_write_hotp_slot(1, b'python_test', bbRFC_SECRET, 0, False, False, True, token_ID, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK for i in range(5): hotp_code = gs(C.NK_get_hotp_code(1)) - assert hotp_code != "" + assert hotp_code != b'' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK @@ -347,11 +349,11 @@ def test_HOTP_counters(C): slot_number = 1 for counter, code in enumerate(HOTP_test_data): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, 'python_test', RFC_SECRET, counter, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, b'python_test', bbRFC_SECRET, counter, use_8_digits, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK r = gs(C.NK_get_hotp_code(slot_number)) code = str(code)[-8:] if use_8_digits else str(code)[-6:] - assert code == r + assert bb(code) == r INT32_MAX = 2 ** 31 - 1 @@ -360,7 +362,7 @@ def test_HOTP_64bit_counter(C): pytest.xfail('bug in NK Storage HOTP firmware - counter is set with a 8 digits string, ' 'however int32max takes 10 digits to be written') oath = pytest.importorskip("oath") - lib_at = lambda t: oath.hotp(RFC_SECRET, t, format='dec6') + lib_at = lambda t: bb(oath.hotp(RFC_SECRET, t, format='dec6')) PIN_protection = False use_8_digits = False slot_number = 1 @@ -371,7 +373,7 @@ def test_HOTP_64bit_counter(C): lib_res = [] for t in range(INT32_MAX - 5, INT32_MAX + 5, 1): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, 'python_test', RFC_SECRET, t, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, b'python_test', bbRFC_SECRET, t, use_8_digits, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code_device = gs(C.NK_get_hotp_code(slot_number)) dev_res += (t, code_device) @@ -384,14 +386,14 @@ def test_TOTP_64bit_time(C): pytest.xfail('bug in NK Storage TOTP firmware') oath = pytest.importorskip("oath") T = 1 - lib_at = lambda t: oath.totp(RFC_SECRET, t=t) + lib_at = lambda t: bb(oath.totp(RFC_SECRET, t=t)) PIN_protection = False slot_number = 1 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_totp_slot(slot_number, 'python_test', RFC_SECRET, 30, False, False, False, "", + assert C.NK_write_totp_slot(slot_number, b'python_test', bbRFC_SECRET, 30, False, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK dev_res = [] lib_res = [] @@ -418,14 +420,14 @@ def test_TOTP_RFC_usepin(C, PIN_protection): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK # test according to https://tools.ietf.org/html/rfc6238#appendix-B assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_totp_slot(slot_number, 'python_test', RFC_SECRET, 30, True, False, False, "", + assert C.NK_write_totp_slot(slot_number, b'python_test', bbRFC_SECRET, 30, True, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK get_func = None if PIN_protection: get_func = lambda x, y, z, r: gs(C.NK_get_totp_code_PIN(x, y, z, r, DefaultPasswords.USER_TEMP)) else: - get_func = lambda x: gs(C.NK_get_totp_code) + get_func = lambda x, y, z, r: gs(C.NK_get_totp_code(x, y, z, r)) # Mode: Sha1, time step X=30 test_data = [ @@ -446,7 +448,7 @@ def test_TOTP_RFC_usepin(C, PIN_protection): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_totp_set_time(t) == DeviceErrorCode.STATUS_OK code_from_device = get_func(slot_number, T, 0, 30) # FIXME T is not changing the outcome - data += [ (t, expected_code) ] + data += [ (t, bb(str(expected_code).zfill(8))) ] responses += [ (t, code_from_device) ] correct += expected_code == code_from_device assert data == responses or correct == len(test_data) @@ -475,12 +477,12 @@ def test_get_OTP_codes(C): assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK for i in range(15): code = gs(C.NK_get_totp_code(i, 0, 0, 0)) - if code == "": + if code == b'': assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED for i in range(3): code = gs(C.NK_get_hotp_code(i)) - if code == "": + if code == b'': assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED @@ -493,31 +495,31 @@ def test_get_OTP_code_from_not_programmed_slot(C): assert C.NK_erase_totp_slot(0, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code = gs(C.NK_get_hotp_code(0)) - assert code == "" + assert code == b'' assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED code = gs(C.NK_get_totp_code(0, 0, 0, 0)) - assert code == "" + assert code == b'' assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED def test_get_code_user_authorize(C): C.NK_set_debug(True) assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_totp_slot(0, 'python_otp_auth', RFC_SECRET, 30, True, False, False, "", + assert C.NK_write_totp_slot(0, b'python_otp_auth', bbRFC_SECRET, 30, True, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK # enable PIN protection of OTP codes with write_config # TODO create convinience function on C API side to enable/disable OTP USER_PIN protection assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_write_config(255, 255, 255, True, False, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code = gs(C.NK_get_totp_code(0, 0, 0, 0)) - assert code == "" + assert code == b'' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_NOT_AUTHORIZED # disable PIN protection with write_config assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code = gs(C.NK_get_totp_code(0, 0, 0, 0)) - assert code != "" + assert code != b'' assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK @@ -547,17 +549,18 @@ def test_read_write_config(C): assert config == (255, 255, 255, False, True) +@pytest.mark.skip def test_factory_reset(C): C.NK_set_debug(True) assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(1, 'python_test', RFC_SECRET, 0, False, False, False, "", + assert C.NK_write_hotp_slot(1, b'python_test', bbRFC_SECRET, 0, False, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert gs(C.NK_get_hotp_code(1)) == "755224" + assert gs(C.NK_get_hotp_code(1)) == b"755224" assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK wait(10) - assert gs(C.NK_get_hotp_code(1)) != "287082" + assert gs(C.NK_get_hotp_code(1)) != b"287082" assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED # restore AES key assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK @@ -593,7 +596,7 @@ def test_OTP_secret_started_from_null(C, secret): skip_if_device_version_lower_than({'P': 8}) oath = pytest.importorskip("oath") - lib_at = lambda t: oath.hotp(secret, t, format='dec6') + lib_at = lambda t: bb(oath.hotp(secret, t, format='dec6')) PIN_protection = False use_8_digits = False slot_number = 1 @@ -604,7 +607,7 @@ def test_OTP_secret_started_from_null(C, secret): lib_res = [] for t in range(1,5): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, 'null_secret', secret, t, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, b'null_secret', bb(secret), t, use_8_digits, False, False, b'', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code_device = gs(C.NK_get_hotp_code(slot_number)) dev_res += (t, code_device) @@ -626,7 +629,7 @@ def test_HOTP_slots_read_write_counter(C, counter): secret = RFC_SECRET oath = pytest.importorskip("oath") - lib_at = lambda t: oath.hotp(secret, t, format='dec6') + lib_at = lambda t: bb(oath.hotp(secret, t, format='dec6')) PIN_protection = False use_8_digits = False assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK @@ -636,7 +639,7 @@ def test_HOTP_slots_read_write_counter(C, counter): lib_res = [] for slot_number in range(3): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, 'HOTP rw' + str(slot_number), secret, counter, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, b'HOTP rw' + bytes(slot_number), bb(secret), counter, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code_device = gs(C.NK_get_hotp_code(slot_number)) dev_res += (counter, code_device) @@ -653,7 +656,7 @@ def test_TOTP_slots_read_write_at_time_period(C, time, period): """ secret = RFC_SECRET oath = pytest.importorskip("oath") - lib_at = lambda t: oath.totp(RFC_SECRET, t=t, period=period) + lib_at = lambda t: bb(oath.totp(RFC_SECRET, t=t, period=period)) PIN_protection = False use_8_digits = False T = 0 @@ -664,7 +667,7 @@ def test_TOTP_slots_read_write_at_time_period(C, time, period): lib_res = [] for slot_number in range(15): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_totp_slot(slot_number, 'TOTP rw' + str(slot_number), secret, period, use_8_digits, False, False, "", + assert C.NK_write_totp_slot(slot_number, b'TOTP rw' + bytes(slot_number), bb(secret), period, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK @@ -688,7 +691,7 @@ def test_TOTP_secrets(C, secret): time = 0 period = 30 oath = pytest.importorskip("oath") - lib_at = lambda t: oath.totp(secret, t=t, period=period) + lib_at = lambda t: bb(oath.totp(secret, t=t, period=period)) PIN_protection = False use_8_digits = False T = 0 @@ -697,7 +700,7 @@ def test_TOTP_secrets(C, secret): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK dev_res = [] lib_res = [] - assert C.NK_write_totp_slot(slot_number, 'secret' + str(len(secret)), secret, period, use_8_digits, False, False, "", + assert C.NK_write_totp_slot(slot_number, b'secret' + bytes(len(secret)), bb(secret), period, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK code_device = gs(C.NK_get_totp_code(slot_number, T, 0, period)) @@ -718,7 +721,7 @@ def test_HOTP_secrets(C, secret): slot_number = 0 counter = 0 oath = pytest.importorskip("oath") - lib_at = lambda t: oath.hotp(secret, counter=t) + lib_at = lambda t: bb(oath.hotp(secret, counter=t)) PIN_protection = False use_8_digits = False T = 0 @@ -727,7 +730,9 @@ def test_HOTP_secrets(C, secret): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK dev_res = [] lib_res = [] - assert C.NK_write_hotp_slot(slot_number, 'secret' + str(len(secret)), secret, counter, use_8_digits, False, False, "", + # repeat authentication for Pro 0.7 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_hotp_slot(slot_number, b'secret' + bytes(len(secret)), bb(secret), counter, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code_device = gs(C.NK_get_hotp_code(slot_number)) dev_res += (counter, code_device) @@ -750,7 +755,7 @@ def test_special_double_press(C): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK for slot_number in range(3): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, 'double' + str(slot_number), secret, counter, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, b'double' + bytes(slot_number), bb(secret), counter, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK # requires manual check @@ -771,17 +776,17 @@ def test_edit_OTP_slot(C): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK slot_number = 0 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - first_name = 'edit slot' - assert C.NK_write_hotp_slot(slot_number, first_name, secret, counter, use_8_digits, False, False, "", + first_name = b'edit slot' + assert C.NK_write_hotp_slot(slot_number, first_name, bb(secret), counter, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert gs(C.NK_get_hotp_slot_name(slot_number)) == first_name first_code = gs(C.NK_get_hotp_code(slot_number)) - changed_name = 'changedname' - empty_secret = '' + changed_name = b'changedname' + empty_secret = b'' assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, changed_name, empty_secret, counter, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, changed_name, empty_secret, counter, use_8_digits, False, False, b"", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK second_code = gs(C.NK_get_hotp_code(slot_number)) assert first_code == second_code @@ -804,6 +809,6 @@ def test_TOTP_codes_from_nitrokeyapp(secret, C): code_device = gs(C.NK_get_totp_code(slot_number, 0, 0, period)) oath = pytest.importorskip("oath") - lib_at = lambda : oath.totp(secret, period=period) + lib_at = lambda : bb(oath.totp(secret, period=period)) print (lib_at()) assert lib_at() == code_device diff --git a/unittest/test_storage.py b/unittest/test_storage.py index da7c9a3..6671f5b 100644 --- a/unittest/test_storage.py +++ b/unittest/test_storage.py @@ -3,7 +3,7 @@ import pprint import pytest from conftest import skip_if_device_version_lower_than -from constants import DefaultPasswords, DeviceErrorCode +from constants import DefaultPasswords, DeviceErrorCode, bb from misc import gs, wait pprint = pprint.PrettyPrinter(indent=4).pprint @@ -28,7 +28,7 @@ def test_get_status_storage(C): assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK status_string = gs(status_pointer) assert len(status_string) > 0 - status_dict = get_dict_from_dissect(status_string) + status_dict = get_dict_from_dissect(status_string.decode('ascii')) default_admin_password_retry_count = 3 assert int(status_dict['AdminPwRetryCount']) == default_admin_password_retry_count @@ -39,7 +39,7 @@ def test_sd_card_usage(C): assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK data_string = gs(data_pointer) assert len(data_string) > 0 - data_dict = get_dict_from_dissect(data_string) + data_dict = get_dict_from_dissect(data_string.decode("ascii")) assert int(data_dict['WriteLevelMax']) <= 100 @@ -51,7 +51,7 @@ def test_encrypted_volume_unlock(C): def test_encrypted_volume_unlock_hidden(C): skip_if_device_version_lower_than({'S': 43}) - hidden_volume_password = 'hiddenpassword' + hidden_volume_password = b'hiddenpassword' assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_create_hidden_volume(0, 20, 21, hidden_volume_password) == DeviceErrorCode.STATUS_OK @@ -61,8 +61,8 @@ def test_encrypted_volume_unlock_hidden(C): def test_encrypted_volume_setup_multiple_hidden_lock(C): import random skip_if_device_version_lower_than({'S': 45}) #hangs device on lower version - hidden_volume_password = 'hiddenpassword' + str(random.randint(0,100)) - p = lambda i: hidden_volume_password + str(i) + hidden_volume_password = b'hiddenpassword' + bb(str(random.randint(0,100))) + p = lambda i: hidden_volume_password + bb(str(i)) assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK for i in range(4): @@ -76,8 +76,8 @@ def test_encrypted_volume_setup_multiple_hidden_lock(C): @pytest.mark.parametrize("volumes_to_setup", range(1, 5)) def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes(C, volumes_to_setup): skip_if_device_version_lower_than({'S': 43}) - hidden_volume_password = 'hiddenpassword' - p = lambda i: hidden_volume_password + str(i) + hidden_volume_password = b'hiddenpassword' + p = lambda i: hidden_volume_password + bb(str(i)) assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK for i in range(volumes_to_setup): @@ -95,8 +95,8 @@ def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes(C, volume @pytest.mark.parametrize("volumes_to_setup", range(1, 5)) def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes_unlock_at_once(C, volumes_to_setup): skip_if_device_version_lower_than({'S': 43}) - hidden_volume_password = 'hiddenpassword' - p = lambda i: hidden_volume_password + str(i) + hidden_volume_password = b'hiddenpassword' + p = lambda i: hidden_volume_password + bb(str(i)) assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK for i in range(volumes_to_setup): @@ -116,8 +116,8 @@ def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes_unlock_at @pytest.mark.parametrize("use_slot", range(4)) def test_encrypted_volume_setup_one_hidden_no_lock_device_slot(C, use_slot): skip_if_device_version_lower_than({'S': 43}) - hidden_volume_password = 'hiddenpassword' - p = lambda i: hidden_volume_password + str(i) + hidden_volume_password = b'hiddenpassword' + p = lambda i: hidden_volume_password + bb(str(i)) assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK i = use_slot @@ -143,7 +143,7 @@ def test_password_safe_slot_name_corruption(C): numbers = '1234567890' * 4 s += numbers[:wid - len(s)] assert len(s) == wid - return s + return bb(s) def get_pass(suffix): return fill('pass' + suffix, 20) @@ -170,8 +170,8 @@ def test_password_safe_slot_name_corruption(C): assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss) assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss) - hidden_volume_password = 'hiddenpassword' - p = lambda i: hidden_volume_password + str(i) + hidden_volume_password = b'hiddenpassword' + p = lambda i: hidden_volume_password + bb(str(i)) def check_volumes_correctness(C): for i in range(volumes_to_setup): assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK @@ -208,8 +208,8 @@ def test_hidden_volume_corruption(C): # bug: this should return error without unlocking encrypted volume each hidden volume lock, but it does not assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK - hidden_volume_password = 'hiddenpassword' - p = lambda i: hidden_volume_password + str(i) + hidden_volume_password = b'hiddenpassword' + p = lambda i: hidden_volume_password + bb(str(i)) for i in range(4): assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK @@ -261,7 +261,7 @@ def test_get_busy_progress_on_idle(C): def test_change_update_password(C): skip_if_device_version_lower_than({'S': 43}) - wrong_password = 'aaaaaaaaaaa' + wrong_password = b'aaaaaaaaaaa' assert C.NK_change_update_password(wrong_password, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD assert C.NK_change_update_password(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_change_update_password(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK |