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 | 
