diff options
-rw-r--r-- | .idea/vcs.xml | 2 | ||||
-rw-r--r-- | NK_C_API.cc | 17 | ||||
-rw-r--r-- | NK_C_API.h | 18 | ||||
-rw-r--r-- | NitrokeyManager.cc | 14 | ||||
-rw-r--r-- | command_id.cc | 4 | ||||
-rw-r--r-- | libnitrokey/NitrokeyManager.h | 4 | ||||
-rw-r--r-- | libnitrokey/command_id.h | 2 | ||||
-rw-r--r-- | libnitrokey/misc.h | 16 | ||||
-rw-r--r-- | libnitrokey/stick10_commands.h | 35 | ||||
-rw-r--r-- | unittest/test_library.py | 2 | ||||
-rw-r--r-- | unittest/test_pro.py | 27 |
11 files changed, 133 insertions, 8 deletions
diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..efdb874 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,7 @@ <project version="4"> <component name="VcsDirectoryMappings"> <mapping directory="$PROJECT_DIR$" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/hidapi" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/unittest/Catch" vcs="Git" /> </component> </project>
\ No newline at end of file diff --git a/NK_C_API.cc b/NK_C_API.cc index 844f653..ec7bc29 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -865,6 +865,23 @@ NK_C_API char* NK_get_SD_usage_data_as_string() { }); } + NK_C_API int NK_enable_firmware_update_pro(const char* update_password){ + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->enable_firmware_update_pro(update_password); + }); +} + + NK_C_API int NK_change_firmware_password_pro(const char *current_firmware_password, const char *new_firmware_password) { + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + m->change_firmware_update_password_pro(current_firmware_password, + new_firmware_password); + }); + } + + + #ifdef __cplusplus } #endif @@ -977,6 +977,24 @@ extern "C" { */ NK_C_API int NK_wink(); + + /** + * Enable update mode on Nitrokey Pro. + * Supported from v0.11. + * @param update_password 20 bytes update password + * @return command processing error code + */ + NK_C_API int NK_enable_firmware_update_pro(const char* update_password); + + /** + * Change update-mode password on Nitrokey Pro. + * Supported from v0.11. + * @param current_firmware_password 20 bytes update password + * @param new_firmware_password 20 bytes update password + * @return command processing error code + */ + NK_C_API int NK_change_firmware_password_pro(const char *current_firmware_password, const char *new_firmware_password); + #ifdef __cplusplus } #endif diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 7b8deaa..ea409ef 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -1167,4 +1167,18 @@ using nitrokey::misc::strcpyT; return data.data(); }; + void NitrokeyManager::enable_firmware_update_pro(const char *firmware_pin) { + auto p = get_payload<FirmwareUpdate>(); + strcpyT(p.firmware_password, firmware_pin); + FirmwareUpdate::CommandTransaction::run(device, p); + } + + void + NitrokeyManager::change_firmware_update_password_pro(const char *firmware_pin_current, const char *firmware_pin_new) { + auto p = get_payload<FirmwarePasswordChange>(); + strcpyT(p.firmware_password_current, firmware_pin_current); + strcpyT(p.firmware_password_new, firmware_pin_new); + FirmwarePasswordChange::CommandTransaction::run(device, p); + } + } diff --git a/command_id.cc b/command_id.cc index a6c2a28..9a329bc 100644 --- a/command_id.cc +++ b/command_id.cc @@ -71,6 +71,10 @@ const char *commandid_to_string(CommandID id) { return "CHANGE_USER_PIN"; case CommandID::CHANGE_ADMIN_PIN: return "CHANGE_ADMIN_PIN"; + case CommandID::FIRMWARE_UPDATE: + return "FIRMWARE_UPDATE"; + case CommandID::FIRMWARE_PASSWORD_CHANGE: + return "FIRMWARE_PASSWORD_CHANGE"; case CommandID::ENABLE_CRYPTED_PARI: return "ENABLE_CRYPTED_PARI"; diff --git a/libnitrokey/NitrokeyManager.h b/libnitrokey/NitrokeyManager.h index 2d8d1b6..0691035 100644 --- a/libnitrokey/NitrokeyManager.h +++ b/libnitrokey/NitrokeyManager.h @@ -295,6 +295,10 @@ char * strndup(const char* str, size_t maxlen); void wink(); stick20::ProductionTest::ResponsePayload production_info(); + + void enable_firmware_update_pro(const char *firmware_pin); + + void change_firmware_update_password_pro(const char *firmware_pin_current, const char *firmware_pin_new); }; } diff --git a/libnitrokey/command_id.h b/libnitrokey/command_id.h index eb0d450..ee6726c 100644 --- a/libnitrokey/command_id.h +++ b/libnitrokey/command_id.h @@ -88,6 +88,8 @@ enum class CommandID : uint8_t { CHANGE_ADMIN_PIN = 0x15, WRITE_TO_SLOT_2 = 0x16, SEND_OTP_DATA = 0x17, + FIRMWARE_UPDATE = 0x19, + FIRMWARE_PASSWORD_CHANGE = 0x1A, ENABLE_CRYPTED_PARI = 0x20, DISABLE_CRYPTED_PARI = 0x20 + 1, diff --git a/libnitrokey/misc.h b/libnitrokey/misc.h index d10c8df..a9c4672 100644 --- a/libnitrokey/misc.h +++ b/libnitrokey/misc.h @@ -67,7 +67,8 @@ private: oss << std::hex << std::setw(sizeof(value)*2) << std::setfill('0') << value; return oss.str(); } - + +#define FIELD_WIDTH_MAX (100) /** * Copies string from pointer to fixed size C-style array. Src needs to be a valid C-string - eg. ended with '\0'. * Throws when source is bigger than destination. @@ -82,12 +83,13 @@ private: // throw EmptySourceStringException(slot_number); return; const size_t s_dest = sizeof dest; - LOG(std::string("strcpyT sizes dest src ") - +std::to_string(s_dest)+ " " - +std::to_string(strlen(src))+ " " - ,nitrokey::log::Loglevel::DEBUG_L2); - if (strlen(src) > s_dest){ - throw TooLongStringException(strlen(src), s_dest, src); + const size_t src_strlen = strnlen(src, FIELD_WIDTH_MAX); + LOG(std::string("strcpyT sizes dest src ") + + std::to_string(s_dest) + " " + + std::to_string(src_strlen) + " " + , nitrokey::log::Loglevel::DEBUG_L2); + if (src_strlen > s_dest){ + throw TooLongStringException(src_strlen, s_dest, src); } strncpy((char*) &dest, src, s_dest); } diff --git a/libnitrokey/stick10_commands.h b/libnitrokey/stick10_commands.h index f2ffba2..178b23f 100644 --- a/libnitrokey/stick10_commands.h +++ b/libnitrokey/stick10_commands.h @@ -882,6 +882,41 @@ class BuildAESKey : Command<CommandID::NEW_AES_KEY> { }; +class FirmwareUpdate : Command<CommandID::FIRMWARE_UPDATE> { +public: + struct CommandPayload { + uint8_t firmware_password[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(firmware_password); + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; + +}; + +class FirmwarePasswordChange : Command<CommandID::FIRMWARE_PASSWORD_CHANGE> { +public: + struct CommandPayload { + uint8_t firmware_password_current[20]; + uint8_t firmware_password_new[20]; + std::string dissect() const { + std::stringstream ss; + print_to_ss_volatile(firmware_password_current); + print_to_ss_volatile(firmware_password_new); + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; + +}; + + } } } diff --git a/unittest/test_library.py b/unittest/test_library.py index f6cf366..f26e587 100644 --- a/unittest/test_library.py +++ b/unittest/test_library.py @@ -41,6 +41,8 @@ def test_too_long_strings(C): long_string) == LibraryErrors.TOO_LONG_STRING assert gs(C.NK_get_hotp_code_PIN(0, long_string)) == b"" assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING + assert C.NK_change_firmware_password_pro(long_string, long_string) == LibraryErrors.TOO_LONG_STRING + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING def test_invalid_slot(C): diff --git a/unittest/test_pro.py b/unittest/test_pro.py index d094dec..2930ed8 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -22,7 +22,7 @@ SPDX-License-Identifier: LGPL-3.0 import pytest from conftest import skip_if_device_version_lower_than -from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bb, bbRFC_SECRET +from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bb, bbRFC_SECRET, LibraryErrors from misc import ffi, gs, wait, cast_pointer_to_tuple, has_binary_counter from misc import is_pro_rtm_07, is_pro_rtm_08, is_storage @@ -939,3 +939,28 @@ def test_TOTP_codes_from_nitrokeyapp(secret, C): def test_get_device_model(C): assert C.NK_get_device_model() != 0 # assert C.NK_get_device_model() != C.NK_DISCONNECTED + + +@pytest.mark.firmware +def test_bootloader_password_change_pro(C): + skip_if_device_version_lower_than({'P': 11}) + assert C.NK_change_firmware_password_pro(b'zxcasd', b'zxcasd') == DeviceErrorCode.WRONG_PASSWORD + + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + + +@pytest.mark.firmware +def test_bootloader_run_pro(C): + skip_if_device_version_lower_than({'P': 11}) + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD + # Not enabled due to lack of side-effect removal at this point + # assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + + +@pytest.mark.firmware +def test_bootloader_password_change_pro_too_long(C): + skip_if_device_version_lower_than({'P': 11}) + long_string = b'a' * 100 + assert C.NK_change_firmware_password_pro(long_string, long_string) == LibraryErrors.TOO_LONG_STRING + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING |