aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorszszszsz <szszszsz@users.noreply.github.com>2016-11-26 19:51:11 +0100
committerGitHub <noreply@github.com>2016-11-26 19:51:11 +0100
commitf60f2cf0144a91769a5fc00fac1314d2e00cdf0d (patch)
treead2a4513b1ad01b225a519ac10cafa3e583a26a1
parentd841239bc9ece1ce969c293783219cceb001fc67 (diff)
parentcdd16f3f184b2745094da39de3f815aea6633fdb (diff)
downloadlibnitrokey-f60f2cf0144a91769a5fc00fac1314d2e00cdf0d.tar.gz
libnitrokey-f60f2cf0144a91769a5fc00fac1314d2e00cdf0d.tar.bz2
Merge pull request #52 from Nitrokey/14-storage_commands
Support Nitrokey Storage
-rw-r--r--.gitignore6
-rw-r--r--CMakeLists.txt5
-rw-r--r--Makefile6
-rw-r--r--NK_C_API.cc97
-rw-r--r--NK_C_API.h123
-rw-r--r--NitrokeyManager.cc106
-rw-r--r--device.cc4
-rw-r--r--include/LongOperationInProgressException.h28
-rw-r--r--include/NitrokeyManager.h28
-rw-r--r--include/command.h84
-rw-r--r--include/command_id.h59
-rw-r--r--include/device.h1
-rw-r--r--include/device_proto.h44
-rw-r--r--include/misc.h30
-rw-r--r--include/stick20_commands.h600
-rw-r--r--misc.cc21
-rw-r--r--unittest/Makefile2
-rw-r--r--unittest/conftest.py42
-rw-r--r--unittest/constants.py33
-rw-r--r--unittest/misc.py40
-rw-r--r--unittest/test2.cc254
-rw-r--r--unittest/test_command_ids_header.h41
-rw-r--r--unittest/test_library.py67
-rw-r--r--unittest/test_pro.py (renamed from unittest/test_bindings.py)172
-rw-r--r--unittest/test_storage.py114
25 files changed, 1444 insertions, 563 deletions
diff --git a/.gitignore b/.gitignore
index 1a8accf..47b7703 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,9 @@
*.sw*
*.log
*.o
+unittest/build/
+*.pyc
+core
+.cache/
+.idea/
+CMakeFiles/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eedfd35..e9cd54f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,9 @@ set(SOURCE_FILES
NitrokeyManager.cc
NK_C_API.cc include/CommandFailedException.h include/LibraryException.h
unittest/test_C_API.cpp
- unittest/catch_main.cpp)
+ unittest/catch_main.cpp
+ unittest/test2.cc
+ include/LongOperationInProgressException.h
+ )
add_executable(libnitrokey ${SOURCE_FILES}) \ No newline at end of file
diff --git a/Makefile b/Makefile
index cf0cf0c..977eae6 100644
--- a/Makefile
+++ b/Makefile
@@ -30,14 +30,14 @@ $(BUILD)/%.o: %.cc $(DEPENDS)
clean:
rm -f $(OBJ)
rm -f $(BUILD)/libnitrokey.so
- make -C unittest clean
+ ${MAKE} -C unittest clean
mrproper: clean
rm -f $(BUILD)/*.d
- make -C unittest mrproper
+ ${MAKE} -C unittest mrproper
unittest: $(BUILD)/libnitrokey.so
- make -C unittest
+ ${MAKE} -C unittest
cd unittest/build && ln -fs ../../build/libnitrokey.so .
.PHONY: all clean mrproper unittest
diff --git a/NK_C_API.cc b/NK_C_API.cc
index 7110fca..d42840b 100644
--- a/NK_C_API.cc
+++ b/NK_C_API.cc
@@ -375,5 +375,102 @@ extern int NK_login_auto() {
});
}
+// storage commands
+
+extern int NK_send_startup(uint64_t seconds_from_epoch){
+ auto m = NitrokeyManager::instance();
+ return get_without_result([&](){
+ m->send_startup(seconds_from_epoch);
+ });
+}
+
+extern int NK_unlock_encrypted_volume(const char* user_pin){
+ auto m = NitrokeyManager::instance();
+ return get_without_result([&](){
+ m->unlock_encrypted_volume(user_pin);
+ });
+}
+
+extern 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);
+ });
+}
+
+extern 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);
+ });
+}
+
+extern 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);
+ });
+}
+
+extern 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);
+ });
+}
+
+extern int NK_export_firmware(const char* admin_pin) {
+ auto m = NitrokeyManager::instance();
+ return get_without_result([&](){
+ m->export_firmware(admin_pin) ;
+ });
+}
+
+extern 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);
+ });
+}
+
+extern 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);
+ });
+}
+
+extern 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);
+ });
+}
+
+extern const char* NK_get_status_storage_as_string() {
+ auto m = NitrokeyManager::instance();
+ return get_with_string_result([&](){
+ return m->get_status_storage_as_string();
+ });
+}
+
+extern 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();
+ });
+}
+
+extern int NK_get_progress_bar_value() {
+ auto m = NitrokeyManager::instance();
+ return get_with_result([&](){
+ return m->get_progress_bar_value();
+ });
+}
+
+
}
diff --git a/NK_C_API.h b/NK_C_API.h
index 728824d..a446a62 100644
--- a/NK_C_API.h
+++ b/NK_C_API.h
@@ -324,6 +324,129 @@ extern int NK_erase_password_safe_slot(uint8_t slot_number);
*/
extern int NK_is_AES_supported(const char *user_password);
+
+
+
+
+/**
+ * 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
+ */
+extern 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
+ */
+extern int NK_unlock_encrypted_volume(const char* user_pin);
+
+/**
+ * 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
+ */
+extern int NK_unlock_hidden_volume(const char* hidden_volume_password);
+
+/**
+ * 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern 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
+ */
+extern int NK_get_progress_bar_value();
+
}
diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc
index 4c2c834..20f4f14 100644
--- a/NitrokeyManager.cc
+++ b/NitrokeyManager.cc
@@ -3,6 +3,7 @@
#include "include/NitrokeyManager.h"
#include "include/LibraryException.h"
#include <algorithm>
+#include "include/misc.h"
namespace nitrokey{
@@ -16,7 +17,7 @@ namespace nitrokey{
nitrokey::log::Log::instance()(std::string("strcpyT sizes dest src ")
+std::to_string(s_dest)+ " "
+std::to_string(strlen(src))+ " "
- ,nitrokey::log::Loglevel::DEBUG);
+ ,nitrokey::log::Loglevel::DEBUG_L2);
if (strlen(src) > s_dest){
throw TooLongStringException(strlen(src), s_dest, src);
}
@@ -94,7 +95,7 @@ namespace nitrokey{
void NitrokeyManager::set_debug(bool state) {
if (state){
- Log::instance().set_loglevel(Loglevel::DEBUG_L2);
+ Log::instance().set_loglevel(Loglevel::DEBUG);
} else {
Log::instance().set_loglevel(Loglevel::ERROR);
}
@@ -304,10 +305,10 @@ namespace nitrokey{
case DeviceModel::STORAGE:
{
auto p = get_payload<ChangeAdminUserPin20Current>();
- strcpyT(p.old_pin, current_PIN);
+ strcpyT(p.password, current_PIN);
p.set_kind(StoKind);
auto p2 = get_payload<ChangeAdminUserPin20New>();
- strcpyT(p2.new_pin, new_PIN);
+ strcpyT(p2.password, new_PIN);
p2.set_kind(StoKind);
ChangeAdminUserPin20Current::CommandTransaction::run(*device, p);
ChangeAdminUserPin20New::CommandTransaction::run(*device, p2);
@@ -420,8 +421,8 @@ namespace nitrokey{
}
case DeviceModel::STORAGE : {
auto p = get_payload<stick20::CreateNewKeys>();
- strcpyT(p.admin_password, admin_password);
- p.setKindPrefixed();
+ strcpyT(p.password, admin_password);
+ p.set_defaults();
stick20::CreateNewKeys::CommandTransaction::run(*device, p);
break;
}
@@ -445,13 +446,13 @@ namespace nitrokey{
}
case DeviceModel::STORAGE : {
auto p2 = get_payload<ChangeAdminUserPin20Current>();
- p2.set_kind(PasswordKind::Admin);
- strcpyT(p2.old_pin, admin_password);
+ p2.set_defaults();
+ strcpyT(p2.password, admin_password);
ChangeAdminUserPin20Current::CommandTransaction::run(*device, p2);
- auto p3 = get_payload<stick20::UnlockUserPassword>();
- p3.set_kind(PasswordKind::Admin);
- strcpyT(p3.user_new_password, new_user_password);
- stick20::UnlockUserPassword::CommandTransaction::run(*device, p3);
+ auto p3 = get_payload<stick20::UnlockUserPin>();
+ p3.set_defaults();
+ strcpyT(p3.password, new_user_password);
+ stick20::UnlockUserPin::CommandTransaction::run(*device, p3);
break;
}
}
@@ -486,4 +487,83 @@ namespace nitrokey{
return true;
}
-}
+ //storage commands
+
+ void NitrokeyManager::send_startup(uint64_t seconds_from_epoch){
+ auto p = get_payload<stick20::SendStartup>();
+// p.set_defaults(); //set current time
+ p.localtime = seconds_from_epoch;
+ stick20::SendStartup::CommandTransaction::run(*device, p);
+ }
+
+ void NitrokeyManager::unlock_encrypted_volume(const char* user_pin){
+ misc::execute_password_command<stick20::EnableEncryptedPartition>(*device, user_pin);
+ }
+
+ void NitrokeyManager::unlock_hidden_volume(const char* hidden_volume_password) {
+ misc::execute_password_command<stick20::EnableHiddenEncryptedPartition>(*device, hidden_volume_password);
+ }
+
+ //TODO check is encrypted volume unlocked before execution
+ //if not return library exception
+ void NitrokeyManager::create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
+ const char *hidden_volume_password) {
+ auto p = get_payload<stick20::SetupHiddenVolume>();
+ p.SlotNr_u8 = slot_nr;
+ p.StartBlockPercent_u8 = start_percent;
+ p.EndBlockPercent_u8 = end_percent;
+ strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password);
+ stick20::SetupHiddenVolume::CommandTransaction::run(*device, p);
+ }
+
+ void NitrokeyManager::set_unencrypted_read_only(const char* user_pin) {
+ misc::execute_password_command<stick20::SendSetReadonlyToUncryptedVolume>(*device, user_pin);
+ }
+
+ void NitrokeyManager::set_unencrypted_read_write(const char* user_pin) {
+ misc::execute_password_command<stick20::SendSetReadwriteToUncryptedVolume>(*device, user_pin);
+ }
+
+ void NitrokeyManager::export_firmware(const char* admin_pin) {
+ misc::execute_password_command<stick20::ExportFirmware>(*device, admin_pin);
+ }
+
+ void NitrokeyManager::clear_new_sd_card_warning(const char* admin_pin) {
+ misc::execute_password_command<stick20::SendClearNewSdCardFound>(*device, admin_pin);
+ }
+
+ void NitrokeyManager::fill_SD_card_with_random_data(const char* admin_pin) {
+ auto p = get_payload<stick20::FillSDCardWithRandomChars>();
+ p.set_defaults();
+ strcpyT(p.admin_pin, admin_pin);
+ stick20::FillSDCardWithRandomChars::CommandTransaction::run(*device, p);
+ }
+
+ void NitrokeyManager::change_update_password(const char* current_update_password, const char* new_update_password) {
+ auto p = get_payload<stick20::ChangeUpdatePassword>();
+ strcpyT(p.current_update_password, current_update_password);
+ strcpyT(p.new_update_password, new_update_password);
+ stick20::ChangeUpdatePassword::CommandTransaction::run(*device, p);
+ }
+
+ const char * NitrokeyManager::get_status_storage_as_string(){
+ auto p = stick20::GetDeviceStatus::CommandTransaction::run(*device);
+ return strdup(p.data().dissect().c_str());
+ }
+
+ const char * NitrokeyManager::get_SD_usage_data_as_string(){
+ auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(*device);
+ return strdup(p.data().dissect().c_str());
+ }
+
+ int NitrokeyManager::get_progress_bar_value(){
+ try{
+ stick20::GetDeviceStatus::CommandTransaction::run(*device);
+ return -1;
+ }
+ catch (LongOperationInProgressException &e){
+ return e.progress_bar_value;
+ }
+ }
+
+ }
diff --git a/device.cc b/device.cc
index 8b40984..8c3c799 100644
--- a/device.cc
+++ b/device.cc
@@ -95,8 +95,8 @@ Stick10::Stick10() {
Stick20::Stick20() {
m_vid = 0x20a0;
m_pid = 0x4109;
- m_retry_timeout = 20ms;
+ m_retry_timeout = 200ms;
m_model = DeviceModel::STORAGE;
- m_send_receive_delay = 20ms;
+ m_send_receive_delay = 200ms;
m_retry_receiving_count = 40;
}
diff --git a/include/LongOperationInProgressException.h b/include/LongOperationInProgressException.h
new file mode 100644
index 0000000..7f182b0
--- /dev/null
+++ b/include/LongOperationInProgressException.h
@@ -0,0 +1,28 @@
+//
+// Created by sz on 24.10.16.
+//
+
+#ifndef LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H
+#define LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H
+
+#include "CommandFailedException.h"
+
+class LongOperationInProgressException : public CommandFailedException {
+
+public:
+ unsigned char progress_bar_value;
+
+ LongOperationInProgressException(
+ unsigned char _command_id, uint8_t last_command_status, unsigned char _progress_bar_value)
+ : CommandFailedException(_command_id, last_command_status), progress_bar_value(_progress_bar_value){
+ nitrokey::log::Log::instance()(
+ std::string("LongOperationInProgressException, progress bar status: ")+
+ std::to_string(progress_bar_value), nitrokey::log::Loglevel::DEBUG);
+ }
+ virtual const char *what() const throw() {
+ return "Device returned busy status with long operation in progress";
+ }
+};
+
+
+#endif //LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H
diff --git a/include/NitrokeyManager.h b/include/NitrokeyManager.h
index 52c18d7..60fa753 100644
--- a/include/NitrokeyManager.h
+++ b/include/NitrokeyManager.h
@@ -82,6 +82,33 @@ namespace nitrokey {
bool is_AES_supported(const char *user_password);
+ void unlock_encrypted_volume(const char *user_password);
+
+ void unlock_hidden_volume(const char *hidden_volume_password);
+
+ void set_unencrypted_read_only(const char *user_pin);
+
+ void set_unencrypted_read_write(const char *user_pin);
+
+ void export_firmware(const char *admin_pin);
+
+ void clear_new_sd_card_warning(const char *admin_pin);
+
+ void fill_SD_card_with_random_data(const char *admin_pin);
+
+ void change_update_password(const char *current_update_password, const char *new_update_password);
+
+ void create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
+ const char *hidden_volume_password);
+
+ void send_startup(uint64_t seconds_from_epoch);
+
+ const char * get_status_storage_as_string();
+
+ const char *get_SD_usage_data_as_string();
+
+ int get_progress_bar_value();
+
~NitrokeyManager();
private:
NitrokeyManager();
@@ -101,7 +128,6 @@ namespace nitrokey {
template <typename ProCommand, PasswordKind StoKind>
void change_PIN_general(char *current_PIN, char *new_PIN);
-
};
}
diff --git a/include/command.h b/include/command.h
index 715902d..0a875e4 100644
--- a/include/command.h
+++ b/include/command.h
@@ -5,19 +5,77 @@
#include "cxx_semantics.h"
namespace nitrokey {
-namespace proto {
-
-template <CommandID cmd_id>
-class Command : semantics::non_constructible {
- public:
- constexpr static CommandID command_id() { return cmd_id; }
-
- template <typename T>
- static std::string dissect(const T &) {
- return std::string("Payload dissection is unavailable");
- }
-};
-}
+ namespace proto {
+
+ template<CommandID cmd_id>
+ class Command : semantics::non_constructible {
+ public:
+ constexpr static CommandID command_id() { return cmd_id; }
+
+ template<typename T>
+ std::string dissect(const T &) {
+ return std::string("Payload dissection is unavailable");
+ }
+ };
+
+#define print_to_ss(x) ( ss << " " << (#x) <<":\t" << (x) << std::endl );
+namespace stick20{
+ enum class PasswordKind : uint8_t {
+ User = 'P',
+ Admin = 'A',
+ AdminPrefixed
+ };
+
+ template<CommandID cmd_id, PasswordKind Tpassword_kind = PasswordKind::User, int password_length = 20>
+ class PasswordCommand : public Command<cmd_id> {
+ public:
+ struct CommandPayload {
+ uint8_t kind;
+ uint8_t password[password_length];
+
+ std::string dissect() const {
+ std::stringstream ss;
+ print_to_ss( kind );
+ print_to_ss(password);
+ return ss.str();
+ }
+ void set_kind_admin() {
+ kind = (uint8_t) 'A';
+ }
+ void set_kind_admin_prefixed() {
+ kind = (uint8_t) 'P';
+ }
+ void set_kind_user() {
+ kind = (uint8_t) 'P';
+ }
+
+ void set_defaults(){
+ set_kind(Tpassword_kind);
+ }
+
+ void set_kind(PasswordKind password_kind){
+ switch (password_kind){
+ case PasswordKind::Admin:
+ set_kind_admin();
+ break;
+ case PasswordKind::User:
+ set_kind_user();
+ break;
+ case PasswordKind::AdminPrefixed:
+ set_kind_admin_prefixed();
+ break;
+ }
+ };
+
+ } __packed;
+
+ typedef Transaction<Command<cmd_id>::command_id(), struct CommandPayload, struct EmptyPayload>
+ CommandTransaction;
+
+ };
+ }
+ }
}
+#undef print_to_ss
#endif
diff --git a/include/command_id.h b/include/command_id.h
index 8148cc1..a3806f0 100644
--- a/include/command_id.h
+++ b/include/command_id.h
@@ -66,43 +66,40 @@ enum class CommandID : uint8_t {
CHANGE_USER_PIN = 0x14,
CHANGE_ADMIN_PIN = 0x15,
- STICK20_CMD_SEND_PASSWORD = stick20::CMD_START_VALUE + 18,
- STICK20_CMD_SEND_NEW_PASSWORD = stick20::CMD_START_VALUE + 19,
-
ENABLE_CRYPTED_PARI = 0x20,
- DISABLE_CRYPTED_PARI,
- ENABLE_HIDDEN_CRYPTED_PARI,
- DISABLE_HIDDEN_CRYPTED_PARI,
- ENABLE_FIRMWARE_UPDATE,
- EXPORT_FIRMWARE_TO_FILE,
- GENERATE_NEW_KEYS,
- FILL_SD_CARD_WITH_RANDOM_CHARS,
+ DISABLE_CRYPTED_PARI = 0x20 + 1, //@unused
+ ENABLE_HIDDEN_CRYPTED_PARI = 0x20 + 2,
+ DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3, //@unused
+ ENABLE_FIRMWARE_UPDATE = 0x20 + 4, //enables update mode
+ EXPORT_FIRMWARE_TO_FILE = 0x20 + 5,
+ GENERATE_NEW_KEYS = 0x20 + 6,
+ FILL_SD_CARD_WITH_RANDOM_CHARS = 0x20 + 7,
- WRITE_STATUS_DATA,
- ENABLE_READONLY_UNCRYPTED_LUN,
- ENABLE_READWRITE_UNCRYPTED_LUN,
+ WRITE_STATUS_DATA = 0x20 + 8, //@unused
+ ENABLE_READONLY_UNCRYPTED_LUN = 0x20 + 9,
+ ENABLE_READWRITE_UNCRYPTED_LUN = 0x20 + 10,
- SEND_PASSWORD_MATRIX,
- SEND_PASSWORD_MATRIX_PINDATA,
- SEND_PASSWORD_MATRIX_SETUP,
+ SEND_PASSWORD_MATRIX = 0x20 + 11, //@unused
+ SEND_PASSWORD_MATRIX_PINDATA = 0x20 + 12, //@unused
+ SEND_PASSWORD_MATRIX_SETUP = 0x20 + 13, //@unused
- GET_DEVICE_STATUS,
- SEND_DEVICE_STATUS,
+ GET_DEVICE_STATUS = 0x20 + 14,
+ SEND_DEVICE_STATUS = 0x20 + 15,
- SEND_HIDDEN_VOLUME_PASSWORD,
- SEND_HIDDEN_VOLUME_SETUP,
- SEND_PASSWORD,
- SEND_NEW_PASSWORD,
- CLEAR_NEW_SD_CARD_FOUND,
+ SEND_HIDDEN_VOLUME_PASSWORD = 0x20 + 16, //@unused
+ SEND_HIDDEN_VOLUME_SETUP = 0x20 + 17,
+ SEND_PASSWORD = 0x20 + 18,
+ SEND_NEW_PASSWORD = 0x20 + 19,
+ CLEAR_NEW_SD_CARD_FOUND = 0x20 + 20,
- SEND_STARTUP,
- SEND_CLEAR_STICK_KEYS_NOT_INITIATED,
- SEND_LOCK_STICK_HARDWARE,
+ SEND_STARTUP = 0x20 + 21,
+ SEND_CLEAR_STICK_KEYS_NOT_INITIATED = 0x20 + 22,
+ SEND_LOCK_STICK_HARDWARE = 0x20 + 23, //locks firmware upgrade
- PRODUCTION_TEST,
- SEND_DEBUG_DATA,
+ PRODUCTION_TEST = 0x20 + 24,
+ SEND_DEBUG_DATA = 0x20 + 25, //@unused
- CHANGE_UPDATE_PIN,
+ CHANGE_UPDATE_PIN = 0x20 + 26,
GET_PW_SAFE_SLOT_STATUS = 0x60,
GET_PW_SAFE_SLOT_NAME = 0x61,
@@ -112,8 +109,8 @@ enum class CommandID : uint8_t {
SET_PW_SAFE_SLOT_DATA_2 = 0x65,
PW_SAFE_ERASE_SLOT = 0x66,
PW_SAFE_ENABLE = 0x67,
- PW_SAFE_INIT_KEY = 0x68,
- PW_SAFE_SEND_DATA = 0x69,
+ PW_SAFE_INIT_KEY = 0x68, //@unused
+ PW_SAFE_SEND_DATA = 0x69, //@unused
SD_CARD_HIGH_WATERMARK = 0x70,
DETECT_SC_AES = 0x6a,
NEW_AES_KEY = 0x6b
diff --git a/include/device.h b/include/device.h
index 34b7a5b..3f18921 100644
--- a/include/device.h
+++ b/include/device.h
@@ -21,6 +21,7 @@ class Device {
public:
Device();
+ virtual ~Device(){disconnect();}
// lack of device is not actually an error,
// so it doesn't throw
diff --git a/include/device_proto.h b/include/device_proto.h
index cde1d51..0953566 100644
--- a/include/device_proto.h
+++ b/include/device_proto.h
@@ -16,6 +16,7 @@
#include "command_id.h"
#include "dissect.h"
#include "CommandFailedException.h"
+#include "LongOperationInProgressException.h"
#define STICK20_UPDATE_MODE_VID 0x03EB
#define STICK20_UPDATE_MODE_PID 0x2FF1
@@ -89,24 +90,38 @@ namespace nitrokey {
* command_id member in incoming HIDReport structure carries the command
* type last used.
*/
+ namespace DeviceResponseConstants{
+ //magic numbers from firmware
+ static constexpr auto storage_status_absolute_address = 21;
+ static constexpr auto storage_data_absolute_address = storage_status_absolute_address + 5;
+ static constexpr auto header_size = 8; //from _zero to last_command_status inclusive
+ static constexpr auto footer_size = 4; //crc
+ static constexpr auto wrapping_size = header_size + footer_size;
+ }
+
template<CommandID cmd_id, typename ResponsePayload>
struct DeviceResponse {
+ static constexpr auto storage_status_padding_size =
+ DeviceResponseConstants::storage_status_absolute_address - DeviceResponseConstants::header_size;
+
uint8_t _zero;
uint8_t device_status;
uint8_t command_id; // originally last_command_type
uint32_t last_command_crc;
uint8_t last_command_status;
+
union {
- uint8_t _padding[HID_REPORT_SIZE - 12];
+ uint8_t _padding[HID_REPORT_SIZE - DeviceResponseConstants::wrapping_size];
ResponsePayload payload;
struct {
- uint8_t _storage_status_padding[20 - 8 + 1]; //starts on 20th byte minus already 8 used + zero byte
+ uint8_t _storage_status_padding[storage_status_padding_size];
uint8_t command_counter;
uint8_t command_id;
uint8_t device_status; //@see stick20::device_status
uint8_t progress_bar_value;
} __packed storage_status;
} __packed;
+
uint32_t crc;
void initialize() { bzero(this, sizeof *this); }
@@ -119,9 +134,7 @@ namespace nitrokey {
}
void update_CRC() { crc = calculate_CRC(); }
-
bool isCRCcorrect() const { return crc == calculate_CRC(); }
-
bool isValid() const {
// return !_zero && payload.isValid() && isCRCcorrect() &&
// command_id == (uint8_t)(cmd_id);
@@ -216,6 +229,7 @@ namespace nitrokey {
if (!outp.isValid()) throw std::runtime_error("Invalid outgoing packet");
+ bool successful_communication = false;
int receiving_retry_counter = 0;
int sending_retry_counter = dev.get_retry_sending_count();
while (sending_retry_counter-- > 0) {
@@ -263,12 +277,21 @@ namespace nitrokey {
//SENDPASSWORD gives wrong CRC , for now rely on !=0 (TODO report)
// if (resp.device_status == 0 && resp.last_command_crc == outp.crc && resp.isCRCcorrect()) break;
if (resp.device_status == static_cast<uint8_t>(stick10::device_status::ok) &&
- resp.last_command_crc == outp.crc && resp.isValid()) break;
+ resp.last_command_crc == outp.crc && resp.isValid()){
+ successful_communication = true;
+ break;
+ }
if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy)) {
receiving_retry_counter++;
Log::instance()("Status busy, not decresing receiving_retry_counter counter: " +
std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2);
}
+ if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy) &&
+ static_cast<stick20::device_status>(resp.storage_status.device_status)
+ == stick20::device_status::busy_progressbar){
+ successful_communication = true;
+ break;
+ }
Log::instance()(std::string("Retry status - dev status, equal crc, correct CRC: ")
+ std::to_string(resp.device_status) + " " +
std::to_string(resp.last_command_crc == outp.crc) +
@@ -282,7 +305,7 @@ namespace nitrokey {
std::this_thread::sleep_for(dev.get_retry_timeout());
continue;
}
- if (resp.device_status == 0 && resp.last_command_crc == outp.crc) break;
+ if (successful_communication) break;
Log::instance()(std::string("Resending (outer loop) "), Loglevel::DEBUG_L2);
Log::instance()(std::string("sending_retry_counter count: ") + std::to_string(sending_retry_counter),
Loglevel::DEBUG);
@@ -293,7 +316,7 @@ namespace nitrokey {
clear_packet(outp);
if (status <= 0)
- throw std::runtime_error(
+ throw std::runtime_error( //FIXME replace with CriticalErrorException
std::string("Device error while executing command ") +
std::to_string(status));
@@ -302,6 +325,13 @@ namespace nitrokey {
Log::instance()(std::string("receiving_retry_counter count: ") + std::to_string(receiving_retry_counter),
Loglevel::DEBUG);
+ if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy) &&
+ static_cast<stick20::device_status>(resp.storage_status.device_status)
+ == stick20::device_status::busy_progressbar){
+ throw LongOperationInProgressException(
+ resp.command_id, resp.device_status, resp.storage_status.progress_bar_value);
+ }
+
if (!resp.isValid()) throw std::runtime_error("Invalid incoming packet");
if (receiving_retry_counter <= 0)
throw std::runtime_error(
diff --git a/include/misc.h b/include/misc.h
index 5fcd16d..5158de0 100644
--- a/include/misc.h
+++ b/include/misc.h
@@ -3,11 +3,32 @@
#include <stdio.h>
#include <string>
#include <vector>
+#include <string.h>
+#include "log.h"
+#include "LibraryException.h"
namespace nitrokey {
namespace misc {
-template <typename T>
+ template <typename T>
+ void strcpyT(T& dest, const char* src){
+
+ if (src == nullptr)
+// throw EmptySourceStringException(slot_number);
+ return;
+ const size_t s_dest = sizeof dest;
+ nitrokey::log::Log::instance()(std::string("strcpyT sizes dest src ")
+ +std::to_string(s_dest)+ " "
+ +std::to_string(strlen(src))+ " "
+ ,nitrokey::log::Loglevel::DEBUG);
+ if (strlen(src) > s_dest){
+ throw TooLongStringException(strlen(src), s_dest, src);
+ }
+ strncpy((char*) &dest, src, s_dest);
+ }
+
+
+ template <typename T>
typename T::CommandPayload get_payload(){
//Create, initialize and return by value command payload
typename T::CommandPayload st;
@@ -15,6 +36,13 @@ typename T::CommandPayload get_payload(){
return st;
}
+ template<typename CMDTYPE, typename Tdev>
+ void execute_password_command(Tdev &stick, const char *password) {
+ auto p = get_payload<CMDTYPE>();
+ p.set_defaults();
+ strcpyT(p.password, password);
+ CMDTYPE::CommandTransaction::run(stick, p);
+ }
std::string hexdump(const char *p, size_t size, bool print_header=true);
uint32_t stm_crc32(const uint8_t *data, size_t size);
diff --git a/include/stick20_commands.h b/include/stick20_commands.h
index f4e7500..1af9da3 100644
--- a/include/stick20_commands.h
+++ b/include/stick20_commands.h
@@ -1,5 +1,6 @@
#ifndef STICK20_COMMANDS_H
#define STICK20_COMMANDS_H
+
#include "inttypes.h"
#include "command.h"
#include <string>
@@ -8,336 +9,299 @@
namespace nitrokey {
-namespace proto {
+ namespace proto {
/*
* STICK20 protocol command ids
* a superset (almost) of STICK10
*/
-namespace stick20 {
-
- enum class PasswordKind : uint8_t {
- User = 'P',
- Admin = 'A'
- };
-
- class ChangeAdminUserPin20Current : Command<CommandID::STICK20_CMD_SEND_PASSWORD> {
- public:
- struct CommandPayload {
- uint8_t kind;
- uint8_t old_pin[20];
- std::string dissect() const {
- std::stringstream ss;
- ss << " old_pin:\t" << old_pin<< std::endl;
- return ss.str();
- }
- void set_kind(PasswordKind k){
- kind = (uint8_t)k;
- }
- } __packed;
-
- typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
- };
-
-
- class ChangeAdminUserPin20New : Command<CommandID::STICK20_CMD_SEND_NEW_PASSWORD> {
- public:
-
- struct CommandPayload {
- uint8_t kind;
- uint8_t new_pin[20];
- std::string dissect() const {
- std::stringstream ss;
- ss << " new_pin:\t" << new_pin<< std::endl;
- return ss.str();
- }
- void set_kind(PasswordKind k){
- kind = (uint8_t)k;
+#define print_to_ss(x) ( ss << " " << (#x) <<":\t" << (x) << std::endl );
+ namespace stick20 {
+
+ class ChangeAdminUserPin20Current :
+ public PasswordCommand<CommandID::SEND_PASSWORD, PasswordKind::Admin> {};
+ class ChangeAdminUserPin20New :
+ public PasswordCommand<CommandID::SEND_NEW_PASSWORD, PasswordKind::Admin> {};
+ class UnlockUserPin :
+ public PasswordCommand<CommandID::UNLOCK_USER_PASSWORD, PasswordKind::Admin> {};
+
+ class EnableEncryptedPartition : public PasswordCommand<CommandID::ENABLE_CRYPTED_PARI> {};
+ class DisableEncryptedPartition : public PasswordCommand<CommandID::DISABLE_CRYPTED_PARI> {};
+ class EnableHiddenEncryptedPartition : public PasswordCommand<CommandID::ENABLE_HIDDEN_CRYPTED_PARI> {};
+ class DisableHiddenEncryptedPartition : public PasswordCommand<CommandID::DISABLE_CRYPTED_PARI> {};
+ class EnableFirmwareUpdate : public PasswordCommand<CommandID::ENABLE_FIRMWARE_UPDATE> {};
+
+ class ChangeUpdatePassword : Command<CommandID::CHANGE_UPDATE_PIN> {
+ public:
+ struct CommandPayload {
+ uint8_t __gap;
+ uint8_t current_update_password[20];
+ uint8_t __gap2;
+ uint8_t new_update_password[20];
+ std::string dissect() const {
+ std::stringstream ss;
+ print_to_ss( current_update_password );
+ print_to_ss( new_update_password );
+ return ss.str();
+ }
+ };
+
+ typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
+ CommandTransaction;
+ };
+
+ class ExportFirmware : public PasswordCommand<CommandID::EXPORT_FIRMWARE_TO_FILE> {};
+
+ class CreateNewKeys :
+ public PasswordCommand<CommandID::GENERATE_NEW_KEYS, PasswordKind::AdminPrefixed, 30> {};
+
+
+ class FillSDCardWithRandomChars : Command<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS> {
+ public:
+ enum class ChosenVolumes : uint8_t {
+ all_volumes = 0,
+ encrypted_volume = 1
+ };
+
+ struct CommandPayload {
+ uint8_t volume_flag;
+ uint8_t kind;
+ uint8_t admin_pin[20];
+
+ std::string dissect() const {
+ std::stringstream ss;
+ print_to_ss( (int) volume_flag );
+ print_to_ss( kind );
+ print_to_ss(admin_pin);
+ return ss.str();
+ }
+ void set_kind_user() {
+ kind = (uint8_t) 'P';
+ }
+ void set_defaults(){
+ set_kind_user();
+ volume_flag = static_cast<uint8_t>(ChosenVolumes::encrypted_volume);
+ }
+
+ } __packed;
+
+ typedef Transaction<Command<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS>::command_id(),
+ struct CommandPayload, struct EmptyPayload>
+ CommandTransaction;
+ };
+
+ namespace StorageCommandResponsePayload{
+ using namespace DeviceResponseConstants;
+ static constexpr auto padding_size =
+ storage_data_absolute_address - header_size;
+ struct TransmissionData{
+ uint8_t _padding[padding_size];
+
+ uint8_t SendCounter_u8;
+ uint8_t SendDataType_u8;
+ uint8_t FollowBytesFlag_u8;
+ uint8_t SendSize_u8;
+
+ std::string dissect() const {
+ std::stringstream ss;
+ ss << "_padding:" << std::endl
+ << ::nitrokey::misc::hexdump((const char *) (_padding),
+ sizeof _padding);
+ print_to_ss((int) SendCounter_u8);
+ print_to_ss((int) SendDataType_u8);
+ print_to_ss((int) FollowBytesFlag_u8);
+ print_to_ss((int) SendSize_u8);
+ return ss.str();
+ }
+
+ } __packed;
}
- } __packed;
-
- typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
- };
-
-
- class UnlockUserPassword : Command<CommandID::UNLOCK_USER_PASSWORD> {
- public:
- struct CommandPayload {
- uint8_t kind;
- uint8_t user_new_password[20];
- std::string dissect() const {
- std::stringstream ss;
- ss << " user_new_password:\t" << user_new_password<< std::endl;
- return ss.str();
- }
- void set_kind(PasswordKind k){
- kind = (uint8_t)k;
- }
- } __packed;
-
- typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
- };
-
-class EnableEncryptedPartition : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30]; // TODO check w/ firmware
- };
-
- typedef Transaction<CommandID::ENABLE_CRYPTED_PARI, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class DisableEncryptedPartition : semantics::non_constructible {
- public:
- typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, struct EmptyPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class EnableHiddenEncryptedPartition : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30]; // TODO check w/ firmware
- };
-
- typedef Transaction<CommandID::ENABLE_HIDDEN_CRYPTED_PARI,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class DisableHiddenEncryptedPartition : semantics::non_constructible {
- public:
- typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, struct EmptyPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class EnableFirmwareUpdate : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30]; // TODO check w/ firmware
- };
-
- typedef Transaction<CommandID::ENABLE_FIRMWARE_UPDATE, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class UpdatePassword : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t old_password[15];
- uint8_t new_password[15];
- };
-
- typedef Transaction<CommandID::CHANGE_UPDATE_PIN, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class ExportFirmware : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::EXPORT_FIRMWARE_TO_FILE, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
- class CreateNewKeys : Command<CommandID::GENERATE_NEW_KEYS> {
- public:
- struct CommandPayload {
- uint8_t kind;
- uint8_t admin_password[30]; //CS20_MAX_PASSWORD_LEN
- std::string dissect() const {
- std::stringstream ss;
- ss << " admin_password:\t" << admin_password<< std::endl;
- return ss.str();
- }
- void setKindPrefixed(){
- kind = 'P';
+ namespace DeviceConfigurationResponsePacket{
+
+ struct ResponsePayload {
+ StorageCommandResponsePayload::TransmissionData transmission_data;
+
+ uint16_t MagicNumber_StickConfig_u16;
+ uint8_t ReadWriteFlagUncryptedVolume_u8;
+ uint8_t ReadWriteFlagCryptedVolume_u8;
+ uint8_t VersionInfo_au8[4];
+ uint8_t ReadWriteFlagHiddenVolume_u8;
+ uint8_t FirmwareLocked_u8;
+ uint8_t NewSDCardFound_u8;
+ uint8_t SDFillWithRandomChars_u8;
+ uint32_t ActiveSD_CardID_u32;
+ uint8_t VolumeActiceFlag_u8;
+ uint8_t NewSmartCardFound_u8;
+ uint8_t UserPwRetryCount;
+ uint8_t AdminPwRetryCount;
+ uint32_t ActiveSmartCardID_u32;
+ uint8_t StickKeysNotInitiated;
+
+ bool isValid() const { return true; }
+
+ std::string dissect() const {
+ std::stringstream ss;
+
+ print_to_ss(transmission_data.dissect());
+ print_to_ss( MagicNumber_StickConfig_u16 );
+ print_to_ss((int) ReadWriteFlagUncryptedVolume_u8 );
+ print_to_ss((int) ReadWriteFlagCryptedVolume_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) SDFillWithRandomChars_u8 );
+ print_to_ss( ActiveSD_CardID_u32 );
+ print_to_ss((int) VolumeActiceFlag_u8 );
+ print_to_ss((int) NewSmartCardFound_u8 );
+ print_to_ss((int) UserPwRetryCount );
+ print_to_ss((int) AdminPwRetryCount );
+ print_to_ss( ActiveSmartCardID_u32 );
+ print_to_ss((int) StickKeysNotInitiated );
+
+ return ss.str();
+ }
+ } __packed;
}
- } __packed;
-
- typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
- };
-
-
-class FillSDCardWithRandomChars : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t volume_flag;
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SetupHiddenVolume : semantics::non_constructible {
- public:
- typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP, struct EmptyPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendPasswordMatrix : semantics::non_constructible {
- public:
- typedef Transaction<CommandID::SEND_PASSWORD_MATRIX, struct EmptyPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendPasswordMatrixPinData : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t pin_data[30]; // TODO how long actually can it be?
- };
-
- typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_PINDATA,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendPasswordMatrixSetup : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t setup_data[30]; // TODO how long actually can it be?
- };
-
- typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_SETUP,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-#define d(x) ss << " "#x":\t" << (int)x << std::endl;
-
- class GetDeviceStatus : Command<CommandID::GET_DEVICE_STATUS> {
- public:
- static const int OUTPUT_CMD_RESULT_STICK20_STATUS_START = 20 +1;
- static const int payload_absolute_begin = 8;
- static const int padding_size = OUTPUT_CMD_RESULT_STICK20_STATUS_START - payload_absolute_begin;
- struct ResponsePayload {
- uint8_t _padding[padding_size]; //TODO confirm padding in Storage firmware
- //data starts from 21st byte of packet -> 13th byte of payload
- uint8_t command_counter;
- uint8_t last_command;
- uint8_t status;
- uint8_t progress_bar_value;
- bool isValid() const { return true; }
-
- std::string dissect() const {
- std::stringstream ss;
- d(command_counter);
- d(last_command);
- d(status);
- d(progress_bar_value);
- ss << "_padding:\t"
- << ::nitrokey::misc::hexdump((const char *)(_padding),
- sizeof _padding);
- return ss.str();
- }
- } __packed;
-
- typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
- CommandTransaction;
- };
-
-
-class SendPassword : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
- typedef Transaction<CommandID::SEND_PASSWORD, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
+ class SendStartup : Command<CommandID::SEND_STARTUP> {
+ public:
+ struct CommandPayload {
+ uint64_t localtime; // POSIX seconds from epoch start, supports until year 2106
+ std::string dissect() const {
+ std::stringstream ss;
+ print_to_ss( localtime );
+ return ss.str();
+ }
+ void set_defaults(){
+ localtime =
+ std::chrono::duration_cast<std::chrono::seconds> (
+ std::chrono::system_clock::now().time_since_epoch()).count();
+ }
+ }__packed;
+
+ using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload;
+
+ typedef Transaction<command_id(), struct CommandPayload, ResponsePayload>
+ CommandTransaction;
+ };
-class SendNewPassword : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::SEND_NEW_PASSWORD, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
// TODO fix original nomenclature
-class SendSetReadonlyToUncryptedVolume : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendSetReadwriteToUncryptedVolume : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendClearNewSdCardFound : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::CLEAR_NEW_SD_CARD_FOUND, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendStartup : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint64_t localtime; // POSIX
- };
-
- typedef Transaction<CommandID::SEND_STARTUP, struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class SendHiddenVolumeSetup : semantics::non_constructible {
- public:
- struct CommandPayload {
- // TODO HiddenVolumeSetup_tst type
- };
-
- typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class LockFirmware : semantics::non_constructible {
- public:
- struct CommandPayload {
- uint8_t password[30];
- };
-
- typedef Transaction<CommandID::SEND_LOCK_STICK_HARDWARE,
- struct CommandPayload,
- struct EmptyPayload> CommandTransaction;
-};
-
-class ProductionTest : semantics::non_constructible {
- public:
- typedef Transaction<CommandID::PRODUCTION_TEST, struct EmptyPayload,
- struct EmptyPayload> CommandTransaction;
-};
-}
-}
+ class SendSetReadonlyToUncryptedVolume : public PasswordCommand<CommandID::ENABLE_READONLY_UNCRYPTED_LUN> {};
+ class SendSetReadwriteToUncryptedVolume : public PasswordCommand<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN> {};
+ class SendClearNewSdCardFound : public PasswordCommand<CommandID::CLEAR_NEW_SD_CARD_FOUND> {};
+
+ class GetDeviceStatus : Command<CommandID::GET_DEVICE_STATUS> {
+ public:
+ using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload;
+
+ typedef Transaction<command_id(), struct EmptyPayload, ResponsePayload>
+ CommandTransaction;
+ };
+
+ class GetSDCardOccupancy : Command<CommandID::SD_CARD_HIGH_WATERMARK> {
+ public:
+ struct ResponsePayload {
+ uint8_t WriteLevelMin;
+ uint8_t WriteLevelMax;
+ uint8_t ReadLevelMin;
+ uint8_t ReadLevelMax;
+ std::string dissect() const {
+ std::stringstream ss;
+ print_to_ss((int) WriteLevelMin);
+ print_to_ss((int) WriteLevelMax);
+ print_to_ss((int) ReadLevelMin);
+ print_to_ss((int) ReadLevelMax);
+ return ss.str();
+ }
+ } __packed;
+
+ typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
+ CommandTransaction;
+ };
+
+
+ class SetupHiddenVolume : Command<CommandID::SEND_HIDDEN_VOLUME_SETUP> {
+ public:
+ constexpr static int MAX_HIDDEN_VOLUME_PASSWORD_SIZE = 20;
+ struct CommandPayload {
+ uint8_t SlotNr_u8;
+ uint8_t StartBlockPercent_u8;
+ uint8_t EndBlockPercent_u8;
+ uint8_t HiddenVolumePassword_au8[MAX_HIDDEN_VOLUME_PASSWORD_SIZE];
+ std::string dissect() const {
+ std::stringstream ss;
+ print_to_ss((int) SlotNr_u8);
+ print_to_ss((int) StartBlockPercent_u8);
+ print_to_ss((int) EndBlockPercent_u8);
+ print_to_ss(HiddenVolumePassword_au8);
+ return ss.str();
+ }
+ } __packed;
+
+ typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
+ CommandTransaction;
+ };
+
+
+//disable this command for now
+// class LockFirmware : public PasswordCommand<CommandID::SEND_LOCK_STICK_HARDWARE> {};
+
+ class ProductionTest : Command<CommandID::PRODUCTION_TEST> {
+ public:
+ struct ResponsePayload {
+
+ StorageCommandResponsePayload::TransmissionData transmission_data;
+
+ uint8_t FirmwareVersion_au8[2]; // 2 byte // 2
+ uint8_t FirmwareVersionInternal_u8; // 1 byte // 3
+ uint8_t SD_Card_Size_u8; // 1 byte // 4
+ uint32_t CPU_CardID_u32; // 4 byte // 8
+ uint32_t SmartCardID_u32; // 4 byte // 12
+ uint32_t SD_CardID_u32; // 4 byte // 16
+ uint8_t SC_UserPwRetryCount; // User PIN retry count 1 byte // 17
+ uint8_t SC_AdminPwRetryCount; // Admin PIN retry count 1 byte // 18
+ uint8_t SD_Card_ManufacturingYear_u8; // 1 byte // 19
+ uint8_t SD_Card_ManufacturingMonth_u8; // 1 byte // 20
+ uint16_t SD_Card_OEM_u16; // 2 byte // 22
+ uint16_t SD_WriteSpeed_u16; // in kbyte / sec 2 byte // 24
+ uint8_t SD_Card_Manufacturer_u8; // 1 byte // 25
+
+ bool isValid() const { return true; }
+
+ std::string dissect() const {
+ std::stringstream ss;
+
+ print_to_ss(transmission_data.dissect());
+ print_to_ss((int) FirmwareVersion_au8[0]);
+ print_to_ss((int) FirmwareVersion_au8[1]);
+ print_to_ss((int) FirmwareVersionInternal_u8);
+ print_to_ss((int) SD_Card_Size_u8);
+ print_to_ss( CPU_CardID_u32);
+ print_to_ss( SmartCardID_u32);
+ print_to_ss( SD_CardID_u32);
+ print_to_ss((int) SC_UserPwRetryCount);
+ print_to_ss((int) SC_AdminPwRetryCount);
+ print_to_ss((int) SD_Card_ManufacturingYear_u8);
+ print_to_ss((int) SD_Card_ManufacturingMonth_u8);
+ print_to_ss( SD_Card_OEM_u16);
+ print_to_ss( SD_WriteSpeed_u16);
+ print_to_ss((int) SD_Card_Manufacturer_u8);
+ return ss.str();
+ }
+
+ } __packed;
+
+ typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
+ CommandTransaction;
+ };
+ }
+ }
}
+#undef print_to_ss
+
#endif
diff --git a/misc.cc b/misc.cc
index 9b339cd..c9d38cb 100644
--- a/misc.cc
+++ b/misc.cc
@@ -34,6 +34,7 @@ std::vector<uint8_t> hex_string_to_byte(const char* hexString){
return data;
};
+#include <cctype>
std::string hexdump(const char *p, size_t size, bool print_header) {
std::stringstream out;
char formatbuf[128];
@@ -45,9 +46,23 @@ std::string hexdump(const char *p, size_t size, bool print_header) {
out << formatbuf;
}
- for (const char *le = p + 16; p < le && p < pend; p++) {
- snprintf(formatbuf, 128, "%02x ", uint8_t(*p));
- out << formatbuf;
+ const char* pp = p;
+ for (const char *le = p + 16; p < le; p++) {
+ if (p < pend){
+ snprintf(formatbuf, 128, "%02x ", uint8_t(*p));
+ out << formatbuf;
+ } else {
+ out << "-- ";
+ }
+
+ }
+ out << "\t";
+
+ for (const char *le = pp + 16; pp < le && pp < pend; pp++) {
+ if (std::isgraph(*pp))
+ out << uint8_t(*pp);
+ else
+ out << '.';
}
out << std::endl;
}
diff --git a/unittest/Makefile b/unittest/Makefile
index 9e8fbc1..dbd003e 100644
--- a/unittest/Makefile
+++ b/unittest/Makefile
@@ -8,7 +8,7 @@ LIB = -L../build
LDLIBS = -lnitrokey
BUILD = build
-CXXFLAGS = -std=c++14 -fPIC
+CXXFLAGS = -std=c++14 -fPIC -Wno-gnu-variable-sized-type-not-at-end
CXXSOURCES = $(wildcard *.cc)
TARGETS = $(CXXSOURCES:%.cc=$(BUILD)/%)
diff --git a/unittest/conftest.py b/unittest/conftest.py
new file mode 100644
index 0000000..68227d5
--- /dev/null
+++ b/unittest/conftest.py
@@ -0,0 +1,42 @@
+import pytest
+
+from misc import ffi
+
+@pytest.fixture(scope="module")
+def C(request):
+ fp = '../NK_C_API.h'
+
+ declarations = []
+ with open(fp, 'r') as f:
+ declarations = f.readlines()
+
+ a = iter(declarations)
+ for declaration in a:
+ if declaration.startswith('extern') and not '"C"' in declaration:
+ declaration = declaration.replace('extern', '').strip()
+ while not ';' in declaration:
+ declaration += (next(a)).strip()
+ print(declaration)
+ ffi.cdef(declaration, override=True)
+
+ C = ffi.dlopen("../build/libnitrokey.so")
+ C.NK_set_debug(False)
+ nk_login = C.NK_login_auto()
+ if nk_login != 1:
+ print('No devices detected!')
+ assert nk_login == 1 # returns 0 if not connected or wrong model or 1 when connected
+
+ # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK
+
+ # C.NK_status()
+
+ def fin():
+ print('\nFinishing connection to device')
+ C.NK_logout()
+ print('Finished')
+
+ request.addfinalizer(fin)
+ C.NK_set_debug(True)
+
+ return C
diff --git a/unittest/constants.py b/unittest/constants.py
new file mode 100644
index 0000000..78a219b
--- /dev/null
+++ b/unittest/constants.py
@@ -0,0 +1,33 @@
+from enum import Enum
+from misc import to_hex
+
+RFC_SECRET_HR = '12345678901234567890'
+RFC_SECRET = to_hex(RFC_SECRET_HR) # '12345678901234567890'
+
+
+# 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 DeviceErrorCode(Enum):
+ STATUS_OK = 0
+ BUSY = 1 # busy or busy progressbar in place of wrong_CRC status
+ NOT_PROGRAMMED = 3
+ WRONG_PASSWORD = 4
+ STATUS_NOT_AUTHORIZED = 5
+ STATUS_AES_DEC_FAILED = 0xa
+
+
+class LibraryErrors(Enum):
+ TOO_LONG_STRING = 200
+ INVALID_SLOT = 201
+ INVALID_HEX_STRING = 202
+ TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203
+
diff --git a/unittest/misc.py b/unittest/misc.py
new file mode 100644
index 0000000..b45436d
--- /dev/null
+++ b/unittest/misc.py
@@ -0,0 +1,40 @@
+import cffi
+
+ffi = cffi.FFI()
+gs = ffi.string
+
+
+def to_hex(s):
+ return "".join("{:02x}".format(ord(c)) for c in s)
+
+
+def wait(t):
+ import time
+ msg = 'Waiting for %d seconds' % t
+ print(msg.center(40, '='))
+ time.sleep(t)
+
+
+def cast_pointer_to_tuple(obj, typen, len):
+ # usage:
+ # config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5)
+ return tuple(ffi.cast("%s [%d]" % (typen, len), obj)[0:len])
+
+def get_firmware_version_from_status(C):
+ status = gs(C.NK_status())
+ status = [s if 'firmware_version' in s else '' for s in status.split('\n')]
+ firmware = status[0].split(':')[1]
+ return firmware
+
+
+def is_pro_rtm_07(C):
+ firmware = get_firmware_version_from_status(C)
+ return '07 00' in firmware
+
+
+def is_storage(C):
+ """
+ exact firmware storage is sent by other function
+ """
+ firmware = get_firmware_version_from_status(C)
+ return '01 00' in firmware \ No newline at end of file
diff --git a/unittest/test2.cc b/unittest/test2.cc
new file mode 100644
index 0000000..00e70e3
--- /dev/null
+++ b/unittest/test2.cc
@@ -0,0 +1,254 @@
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
+
+static const char *const default_admin_pin = "12345678";
+static const char *const default_user_pin = "123456";
+
+#include "catch.hpp"
+
+#include <iostream>
+#include <string.h>
+#include <NitrokeyManager.h>
+#include "device_proto.h"
+#include "log.h"
+//#include "stick10_commands.h"
+#include "stick20_commands.h"
+
+using namespace std;
+using namespace nitrokey::device;
+using namespace nitrokey::proto;
+using namespace nitrokey::proto::stick20;
+using namespace nitrokey::log;
+using namespace nitrokey::misc;
+
+
+template<typename CMDTYPE>
+void execute_password_command(Device &stick, const char *password, const char kind = 'P') {
+ auto p = get_payload<CMDTYPE>();
+ if (kind == 'P'){
+ p.set_kind_user();
+ } else {
+ p.set_kind_admin();
+ }
+ strcpyT(p.password, password);
+ CMDTYPE::CommandTransaction::run(stick, p);
+ this_thread::sleep_for(1000ms);
+}
+
+/**
+ * fail on purpose (will result in failed test)
+ * disable from running unwillingly
+ */
+void SKIP_TEST() {
+ CAPTURE("Failing current test to SKIP it");
+ REQUIRE(false);
+}
+
+
+TEST_CASE("long operation test", "[test_long]") {
+ SKIP_TEST();
+
+ Stick20 stick;
+ bool connected = stick.connect();
+ REQUIRE(connected == true);
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+ try{
+ auto p = get_payload<FillSDCardWithRandomChars>();
+ p.set_defaults();
+ strcpyT(p.admin_pin, default_admin_pin);
+ FillSDCardWithRandomChars::CommandTransaction::run(stick, p);
+ this_thread::sleep_for(1000ms);
+
+ CHECK(false);
+ }
+ catch (LongOperationInProgressException &progressException){
+ CHECK(true);
+ }
+
+
+ for (int i = 0; i < 30; ++i) {
+ try {
+ stick10::GetStatus::CommandTransaction::run(stick);
+ }
+ catch (LongOperationInProgressException &progressException){
+ CHECK((int)progressException.progress_bar_value>=0);
+ CAPTURE((int)progressException.progress_bar_value);
+ this_thread::sleep_for(2000ms);
+ }
+
+ }
+
+}
+
+
+#include "test_command_ids_header.h"
+
+TEST_CASE("test device commands ids", "[fast]") {
+
+// REQUIRE(STICK20_CMD_START_VALUE == static_cast<uint8_t>(CommandID::START_VALUE));
+ REQUIRE(STICK20_CMD_ENABLE_CRYPTED_PARI == static_cast<uint8_t>(CommandID::ENABLE_CRYPTED_PARI));
+ REQUIRE(STICK20_CMD_DISABLE_CRYPTED_PARI == static_cast<uint8_t>(CommandID::DISABLE_CRYPTED_PARI));
+ REQUIRE(STICK20_CMD_ENABLE_HIDDEN_CRYPTED_PARI == static_cast<uint8_t>(CommandID::ENABLE_HIDDEN_CRYPTED_PARI));
+ REQUIRE(STICK20_CMD_DISABLE_HIDDEN_CRYPTED_PARI == static_cast<uint8_t>(CommandID::DISABLE_HIDDEN_CRYPTED_PARI));
+ REQUIRE(STICK20_CMD_ENABLE_FIRMWARE_UPDATE == static_cast<uint8_t>(CommandID::ENABLE_FIRMWARE_UPDATE));
+ REQUIRE(STICK20_CMD_EXPORT_FIRMWARE_TO_FILE == static_cast<uint8_t>(CommandID::EXPORT_FIRMWARE_TO_FILE));
+ REQUIRE(STICK20_CMD_GENERATE_NEW_KEYS == static_cast<uint8_t>(CommandID::GENERATE_NEW_KEYS));
+ REQUIRE(STICK20_CMD_FILL_SD_CARD_WITH_RANDOM_CHARS == static_cast<uint8_t>(CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS));
+
+ REQUIRE(STICK20_CMD_WRITE_STATUS_DATA == static_cast<uint8_t>(CommandID::WRITE_STATUS_DATA));
+ REQUIRE(STICK20_CMD_ENABLE_READONLY_UNCRYPTED_LUN == static_cast<uint8_t>(CommandID::ENABLE_READONLY_UNCRYPTED_LUN));
+ REQUIRE(STICK20_CMD_ENABLE_READWRITE_UNCRYPTED_LUN == static_cast<uint8_t>(CommandID::ENABLE_READWRITE_UNCRYPTED_LUN));
+
+ REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX == static_cast<uint8_t>(CommandID::SEND_PASSWORD_MATRIX));
+ REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX_PINDATA == static_cast<uint8_t>(CommandID::SEND_PASSWORD_MATRIX_PINDATA));
+ REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX_SETUP == static_cast<uint8_t>(CommandID::SEND_PASSWORD_MATRIX_SETUP));
+
+ REQUIRE(STICK20_CMD_GET_DEVICE_STATUS == static_cast<uint8_t>(CommandID::GET_DEVICE_STATUS));
+ REQUIRE(STICK20_CMD_SEND_DEVICE_STATUS == static_cast<uint8_t>(CommandID::SEND_DEVICE_STATUS));
+
+ REQUIRE(STICK20_CMD_SEND_HIDDEN_VOLUME_PASSWORD == static_cast<uint8_t>(CommandID::SEND_HIDDEN_VOLUME_PASSWORD));
+ REQUIRE(STICK20_CMD_SEND_HIDDEN_VOLUME_SETUP == static_cast<uint8_t>(CommandID::SEND_HIDDEN_VOLUME_SETUP));
+ REQUIRE(STICK20_CMD_SEND_PASSWORD == static_cast<uint8_t>(CommandID::SEND_PASSWORD));
+ REQUIRE(STICK20_CMD_SEND_NEW_PASSWORD == static_cast<uint8_t>(CommandID::SEND_NEW_PASSWORD));
+ REQUIRE(STICK20_CMD_CLEAR_NEW_SD_CARD_FOUND == static_cast<uint8_t>(CommandID::CLEAR_NEW_SD_CARD_FOUND));
+
+ REQUIRE(STICK20_CMD_SEND_STARTUP == static_cast<uint8_t>(CommandID::SEND_STARTUP));
+ REQUIRE(STICK20_CMD_SEND_CLEAR_STICK_KEYS_NOT_INITIATED == static_cast<uint8_t>(CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED));
+ REQUIRE(STICK20_CMD_SEND_LOCK_STICK_HARDWARE == static_cast<uint8_t>(CommandID::SEND_LOCK_STICK_HARDWARE));
+
+ REQUIRE(STICK20_CMD_PRODUCTION_TEST == static_cast<uint8_t>(CommandID::PRODUCTION_TEST));
+ REQUIRE(STICK20_CMD_SEND_DEBUG_DATA == static_cast<uint8_t>(CommandID::SEND_DEBUG_DATA));
+
+ REQUIRE(STICK20_CMD_CHANGE_UPDATE_PIN == static_cast<uint8_t>(CommandID::CHANGE_UPDATE_PIN));
+
+}
+
+TEST_CASE("test device internal status with various commands", "[fast]") {
+ Stick20 stick;
+ bool connected = stick.connect();
+ REQUIRE(connected == true);
+
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+ auto p = get_payload<stick20::SendStartup>();
+ p.set_defaults();
+ auto device_status = stick20::SendStartup::CommandTransaction::run(stick, p);
+ REQUIRE(device_status.data().AdminPwRetryCount == 3);
+ REQUIRE(device_status.data().UserPwRetryCount == 3);
+ REQUIRE(device_status.data().ActiveSmartCardID_u32 != 0);
+
+ auto production_status = stick20::ProductionTest::CommandTransaction::run(stick);
+ REQUIRE(production_status.data().SD_Card_Size_u8 == 8);
+ REQUIRE(production_status.data().SD_CardID_u32 != 0);
+
+ auto sdcard_occupancy = stick20::GetSDCardOccupancy::CommandTransaction::run(stick);
+ REQUIRE((int) sdcard_occupancy.data().ReadLevelMin >= 0);
+ REQUIRE((int) sdcard_occupancy.data().ReadLevelMax <= 100);
+ REQUIRE((int) sdcard_occupancy.data().WriteLevelMin >= 0);
+ REQUIRE((int) sdcard_occupancy.data().WriteLevelMax <= 100);
+}
+
+TEST_CASE("setup hidden volume test", "[hidden]") {
+ Stick20 stick;
+ bool connected = stick.connect();
+ REQUIRE(connected == true);
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+ stick10::LockDevice::CommandTransaction::run(stick);
+ this_thread::sleep_for(2000ms);
+
+ auto user_pin = default_user_pin;
+ execute_password_command<EnableEncryptedPartition>(stick, user_pin);
+
+ auto p = get_payload<stick20::SetupHiddenVolume>();
+ p.SlotNr_u8 = 0;
+ p.StartBlockPercent_u8 = 70;
+ p.EndBlockPercent_u8 = 90;
+ auto hidden_volume_password = "123123123";
+ strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password);
+ stick20::SetupHiddenVolume::CommandTransaction::run(stick, p);
+ this_thread::sleep_for(2000ms);
+
+ execute_password_command<EnableHiddenEncryptedPartition>(stick, hidden_volume_password);
+}
+
+TEST_CASE("setup multiple hidden volumes", "[hidden]") {
+ Stick20 stick;
+ bool connected = stick.connect();
+ REQUIRE(connected == true);
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+
+ auto user_pin = default_user_pin;
+ stick10::LockDevice::CommandTransaction::run(stick);
+ this_thread::sleep_for(2000ms);
+ execute_password_command<EnableEncryptedPartition>(stick, user_pin);
+
+ constexpr int volume_count = 4;
+ for (int i = 0; i < volume_count; ++i) {
+ auto p = get_payload<stick20::SetupHiddenVolume>();
+ p.SlotNr_u8 = i;
+ p.StartBlockPercent_u8 = 20 + 10*i;
+ p.EndBlockPercent_u8 = p.StartBlockPercent_u8+i+1;
+ auto hidden_volume_password = std::string("123123123")+std::to_string(i);
+ strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password.c_str());
+ stick20::SetupHiddenVolume::CommandTransaction::run(stick, p);
+ this_thread::sleep_for(2000ms);
+ }
+
+
+ for (int i = 0; i < volume_count; ++i) {
+ execute_password_command<EnableEncryptedPartition>(stick, user_pin);
+ auto hidden_volume_password = std::string("123123123")+std::to_string(i);
+ execute_password_command<EnableHiddenEncryptedPartition>(stick, hidden_volume_password.c_str());
+ this_thread::sleep_for(2000ms);
+ }
+}
+
+
+//in case of a bug this could change update PIN to some unexpected value
+// - please save log with packet dump if this test will not pass
+TEST_CASE("update password change", "[dangerous]") {
+ SKIP_TEST();
+
+ Stick20 stick;
+ bool connected = stick.connect();
+ REQUIRE(connected == true);
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+
+ auto pass1 = default_admin_pin;
+ auto pass2 = "12345678901234567890";
+
+ auto data = {
+ make_pair(pass1, pass2),
+ make_pair(pass2, pass1),
+ };
+ for (auto && password: data) {
+ auto p = get_payload<stick20::ChangeUpdatePassword>();
+ strcpyT(p.current_update_password, password.first);
+ strcpyT(p.new_update_password, password.second);
+ stick20::ChangeUpdatePassword::CommandTransaction::run(stick, p);
+ }
+}
+
+TEST_CASE("general test", "[test]") {
+ Stick20 stick;
+ bool connected = stick.connect();
+ REQUIRE(connected == true);
+
+ Log::instance().set_loglevel(Loglevel::DEBUG);
+
+ stick10::LockDevice::CommandTransaction::run(stick);
+// execute_password_command<EnableEncryptedPartition>(stick, "123456");
+// execute_password_command<DisableEncryptedPartition>(stick, "123456");
+// execute_password_command<DisableHiddenEncryptedPartition>(stick, "123123123");
+
+ execute_password_command<SendSetReadonlyToUncryptedVolume>(stick, default_user_pin);
+ execute_password_command<SendSetReadwriteToUncryptedVolume>(stick, default_user_pin);
+ execute_password_command<SendClearNewSdCardFound>(stick, default_admin_pin, 'A');
+ stick20::GetDeviceStatus::CommandTransaction::run(stick);
+ this_thread::sleep_for(1000ms);
+// execute_password_command<LockFirmware>(stick, "123123123"); //CAUTION
+// execute_password_command<EnableFirmwareUpdate>(stick, "123123123"); //CAUTION FIRMWARE PIN
+
+ execute_password_command<ExportFirmware>(stick, "12345678", 'A');
+// execute_password_command<FillSDCardWithRandomChars>(stick, "12345678", 'A');
+
+ stick10::LockDevice::CommandTransaction::run(stick);
+}
diff --git a/unittest/test_command_ids_header.h b/unittest/test_command_ids_header.h
new file mode 100644
index 0000000..cd55c8a
--- /dev/null
+++ b/unittest/test_command_ids_header.h
@@ -0,0 +1,41 @@
+#ifndef LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H
+#define LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H
+
+#define STICK20_CMD_START_VALUE 0x20
+#define STICK20_CMD_ENABLE_CRYPTED_PARI (STICK20_CMD_START_VALUE + 0)
+#define STICK20_CMD_DISABLE_CRYPTED_PARI (STICK20_CMD_START_VALUE + 1)
+#define STICK20_CMD_ENABLE_HIDDEN_CRYPTED_PARI (STICK20_CMD_START_VALUE + 2)
+#define STICK20_CMD_DISABLE_HIDDEN_CRYPTED_PARI (STICK20_CMD_START_VALUE + 3)
+#define STICK20_CMD_ENABLE_FIRMWARE_UPDATE (STICK20_CMD_START_VALUE + 4)
+#define STICK20_CMD_EXPORT_FIRMWARE_TO_FILE (STICK20_CMD_START_VALUE + 5)
+#define STICK20_CMD_GENERATE_NEW_KEYS (STICK20_CMD_START_VALUE + 6)
+#define STICK20_CMD_FILL_SD_CARD_WITH_RANDOM_CHARS (STICK20_CMD_START_VALUE + 7)
+
+#define STICK20_CMD_WRITE_STATUS_DATA (STICK20_CMD_START_VALUE + 8)
+#define STICK20_CMD_ENABLE_READONLY_UNCRYPTED_LUN (STICK20_CMD_START_VALUE + 9)
+#define STICK20_CMD_ENABLE_READWRITE_UNCRYPTED_LUN (STICK20_CMD_START_VALUE + 10)
+
+#define STICK20_CMD_SEND_PASSWORD_MATRIX (STICK20_CMD_START_VALUE + 11)
+#define STICK20_CMD_SEND_PASSWORD_MATRIX_PINDATA (STICK20_CMD_START_VALUE + 12)
+#define STICK20_CMD_SEND_PASSWORD_MATRIX_SETUP (STICK20_CMD_START_VALUE + 13)
+
+#define STICK20_CMD_GET_DEVICE_STATUS (STICK20_CMD_START_VALUE + 14)
+#define STICK20_CMD_SEND_DEVICE_STATUS (STICK20_CMD_START_VALUE + 15)
+
+#define STICK20_CMD_SEND_HIDDEN_VOLUME_PASSWORD (STICK20_CMD_START_VALUE + 16)
+#define STICK20_CMD_SEND_HIDDEN_VOLUME_SETUP (STICK20_CMD_START_VALUE + 17)
+#define STICK20_CMD_SEND_PASSWORD (STICK20_CMD_START_VALUE + 18)
+#define STICK20_CMD_SEND_NEW_PASSWORD (STICK20_CMD_START_VALUE + 19)
+#define STICK20_CMD_CLEAR_NEW_SD_CARD_FOUND (STICK20_CMD_START_VALUE + 20)
+
+#define STICK20_CMD_SEND_STARTUP (STICK20_CMD_START_VALUE + 21)
+#define STICK20_CMD_SEND_CLEAR_STICK_KEYS_NOT_INITIATED (STICK20_CMD_START_VALUE + 22)
+#define STICK20_CMD_SEND_LOCK_STICK_HARDWARE (STICK20_CMD_START_VALUE + 23)
+
+#define STICK20_CMD_PRODUCTION_TEST (STICK20_CMD_START_VALUE + 24)
+#define STICK20_CMD_SEND_DEBUG_DATA (STICK20_CMD_START_VALUE + 25)
+
+#define STICK20_CMD_CHANGE_UPDATE_PIN (STICK20_CMD_START_VALUE + 26)
+
+
+#endif //LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H
diff --git a/unittest/test_library.py b/unittest/test_library.py
new file mode 100644
index 0000000..d0eef80
--- /dev/null
+++ b/unittest/test_library.py
@@ -0,0 +1,67 @@
+import pytest
+
+from misc import ffi, gs, to_hex
+from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors
+
+def test_too_long_strings(C):
+ new_password = '123123123'
+ long_string = 'a' * 100
+ assert C.NK_change_user_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_change_user_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_change_admin_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_change_admin_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_first_authenticate(long_string, DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_erase_totp_slot(0, long_string) == LibraryErrors.TOO_LONG_STRING
+ digits = False
+ assert C.NK_write_hotp_slot(1, long_string, RFC_SECRET, 0, digits, False, False, "",
+ DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_write_hotp_slot(1, 'long_test', RFC_SECRET, 0, digits, False, False, "",
+ long_string) == LibraryErrors.TOO_LONG_STRING
+ assert C.NK_get_hotp_code_PIN(0, long_string) == 0
+ assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING
+
+
+def test_invalid_slot(C):
+ invalid_slot = 255
+ assert C.NK_erase_totp_slot(invalid_slot, 'some password') == LibraryErrors.INVALID_SLOT
+ assert C.NK_write_hotp_slot(invalid_slot, 'long_test', RFC_SECRET, 0, False, False, False, "",
+ 'aaa') == LibraryErrors.INVALID_SLOT
+ assert C.NK_get_hotp_code_PIN(invalid_slot, 'some password') == 0
+ assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
+ assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT
+ assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ assert gs(C.NK_get_password_safe_slot_name(invalid_slot)) == ''
+ assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
+ assert gs(C.NK_get_password_safe_slot_login(invalid_slot)) == ''
+ assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
+
+@pytest.mark.parametrize("invalid_hex_string",
+ ['text', '00 ', '0xff', 'zzzzzzzzzzzz', 'fff', '', 'f' * 257, 'f' * 258])
+def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string):
+ """
+ Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number,
+ empty or longer than 255 characters.
+ """
+ assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '',
+ DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING
+ assert C.NK_write_totp_slot(1, 'python_test', invalid_hex_string, 30, True, False, False, "",
+ DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING
+
+
+def test_warning_binary_bigger_than_secret_buffer(C):
+ invalid_hex_string = to_hex('1234567890') * 3
+ assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '',
+ DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE
+
+
+@pytest.mark.xfail(reason="TODO")
+def test_OTP_secret_started_from_null(C):
+ assert False
+
+
+@pytest.mark.skip(reason='Experimental')
+def test_clear(C):
+ d = 'asdasdasd'
+ print(d)
+ C.clear_password(d)
+ print(d) \ No newline at end of file
diff --git a/unittest/test_bindings.py b/unittest/test_pro.py
index f7ade46..6ab2af9 100644
--- a/unittest/test_bindings.py
+++ b/unittest/test_pro.py
@@ -1,109 +1,8 @@
import pytest
-import cffi
-from enum import Enum
-
-ffi = cffi.FFI()
-gs = ffi.string
-
-
-def to_hex(s):
- return "".join("{:02x}".format(ord(c)) for c in s)
-
-
-def wait(t):
- import time
- msg = 'Waiting for %d seconds' % t
- print(msg.center(40, '='))
- time.sleep(t)
-
-
-RFC_SECRET_HR = '12345678901234567890'
-RFC_SECRET = to_hex(RFC_SECRET_HR) # '12345678901234567890'
-
-
-# print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) )
-
-class DefaultPasswords(Enum):
- ADMIN = '12345678'
- USER = '123456'
- ADMIN_TEMP = '123123123'
- USER_TEMP = '234234234'
-
-
-class DeviceErrorCode(Enum):
- STATUS_OK = 0
- NOT_PROGRAMMED = 3
- WRONG_PASSWORD = 4
- STATUS_NOT_AUTHORIZED = 5
- STATUS_AES_DEC_FAILED = 0xa
-
-
-class LibraryErrors(Enum):
- TOO_LONG_STRING = 200
- INVALID_SLOT = 201
- INVALID_HEX_STRING = 202
- TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203
-
-
-@pytest.fixture(scope="module")
-def C(request):
- fp = '../NK_C_API.h'
-
- declarations = []
- with open(fp, 'r') as f:
- declarations = f.readlines()
-
- a = iter(declarations)
- for declaration in a:
- if declaration.startswith('extern') and not '"C"' in declaration:
- declaration = declaration.replace('extern', '').strip()
- while not ';' in declaration:
- declaration += (next(a)).strip()
- print(declaration)
- ffi.cdef(declaration)
-
- C = ffi.dlopen("../build/libnitrokey.so")
- C.NK_set_debug(False)
- nk_login = C.NK_login_auto()
- if nk_login != 1:
- print('No devices detected!')
- assert nk_login == 1 # returns 0 if not connected or wrong model or 1 when connected
-
- # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
- # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK
-
- # C.NK_status()
-
- def fin():
- print('\nFinishing connection to device')
- C.NK_logout()
- print('Finished')
-
- request.addfinalizer(fin)
- C.NK_set_debug(True)
-
- return C
-
-
-def get_firmware_version_from_status(C):
- status = gs(C.NK_status())
- status = [s if 'firmware_version' in s else '' for s in status.split('\n')]
- firmware = status[0].split(':')[1]
- return firmware
-
-
-def is_pro_rtm_07(C):
- firmware = get_firmware_version_from_status(C)
- return '07 00' in firmware
-
-
-def is_storage(C):
- """
- exact firmware storage is sent by other function
- """
- firmware = get_firmware_version_from_status(C)
- return '01 00' in firmware
+from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET
+from misc import ffi, gs, wait, cast_pointer_to_tuple
+from misc import is_pro_rtm_07, is_storage
def test_enable_password_safe(C):
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
@@ -232,39 +131,6 @@ def test_user_PIN_change(C):
assert C.NK_change_user_PIN(new_password, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
-def test_too_long_strings(C):
- new_password = '123123123'
- long_string = 'a' * 100
- assert C.NK_change_user_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_change_user_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_change_admin_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_change_admin_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_first_authenticate(long_string, DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_erase_totp_slot(0, long_string) == LibraryErrors.TOO_LONG_STRING
- digits = False
- assert C.NK_write_hotp_slot(1, long_string, RFC_SECRET, 0, digits, False, False, "",
- DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_write_hotp_slot(1, 'long_test', RFC_SECRET, 0, digits, False, False, "",
- long_string) == LibraryErrors.TOO_LONG_STRING
- assert C.NK_get_hotp_code_PIN(0, long_string) == 0
- assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING
-
-
-def test_invalid_slot(C):
- invalid_slot = 255
- assert C.NK_erase_totp_slot(invalid_slot, 'some password') == LibraryErrors.INVALID_SLOT
- assert C.NK_write_hotp_slot(invalid_slot, 'long_test', RFC_SECRET, 0, False, False, False, "",
- 'aaa') == LibraryErrors.INVALID_SLOT
- assert C.NK_get_hotp_code_PIN(invalid_slot, 'some password') == 0
- assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
- assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT
- assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
- assert gs(C.NK_get_password_safe_slot_name(invalid_slot)) == ''
- assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
- assert gs(C.NK_get_password_safe_slot_login(invalid_slot)) == ''
- assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
-
-
def test_admin_retry_counts(C):
default_admin_retry_count = 3
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
@@ -609,14 +475,6 @@ def test_factory_reset(C):
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
-@pytest.mark.skip(reason='Experimental')
-def test_clear(C):
- d = 'asdasdasd'
- print(d)
- C.clear_password(d)
- print(d)
-
-
def test_get_status(C):
status = C.NK_status()
s = gs(status)
@@ -628,27 +486,3 @@ def test_get_serial_number(C):
sn = gs(sn)
assert len(sn) > 0
print(('Serial number of the device: ', sn))
-
-
-@pytest.mark.parametrize("invalid_hex_string",
- ['text', '00 ', '0xff', 'zzzzzzzzzzzz', 'fff', '', 'f' * 257, 'f' * 258])
-def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string):
- """
- Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number,
- empty or longer than 255 characters.
- """
- assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '',
- DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING
- assert C.NK_write_totp_slot(1, 'python_test', invalid_hex_string, 30, True, False, False, "",
- DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING
-
-
-def test_warning_binary_bigger_than_secret_buffer(C):
- invalid_hex_string = to_hex('1234567890') * 3
- assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '',
- DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE
-
-
-@pytest.mark.xfail(reason="TODO")
-def test_OTP_secret_started_from_null(C):
- assert False
diff --git a/unittest/test_storage.py b/unittest/test_storage.py
new file mode 100644
index 0000000..01276ce
--- /dev/null
+++ b/unittest/test_storage.py
@@ -0,0 +1,114 @@
+import pytest
+
+from misc import ffi, gs, wait, cast_pointer_to_tuple
+from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors
+
+import pprint
+pprint = pprint.PrettyPrinter(indent=4).pprint
+
+
+def get_dict_from_dissect(status):
+ x = []
+ for s in status.split('\n'):
+ try:
+ if not ':' in s: continue
+ ss = s.replace('\t', '').replace(' (int) ', '').split(':')
+ if not len(ss) == 2: continue
+ x.append(ss)
+ except:
+ pass
+ d = {k.strip(): v.strip() for k, v in x}
+ return d
+
+
+def test_get_status_storage(C):
+ status_pointer = C.NK_get_status_storage_as_string()
+ 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)
+ default_admin_password_retry_count = 3
+ assert int(status_dict['AdminPwRetryCount']) == default_admin_password_retry_count
+
+
+def test_sd_card_usage(C):
+ data_pointer = C.NK_get_SD_usage_data_as_string()
+ 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)
+ assert int(data_dict['WriteLevelMax']) <= 100
+
+
+def test_encrypted_volume_unlock(C):
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+
+def test_encrypted_volume_unlock_hidden(C):
+ hidden_volume_password = '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
+ assert C.NK_unlock_hidden_volume(hidden_volume_password) == DeviceErrorCode.STATUS_OK
+
+@pytest.mark.skip(reason='hangs device, to report')
+def test_encrypted_volume_setup_multiple_hidden(C):
+ hidden_volume_password = 'hiddenpassword'
+ p = lambda i: hidden_volume_password + 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):
+ assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i) ) == DeviceErrorCode.STATUS_OK
+ for i in range(4):
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+
+
+def test_unencrypted_volume_set_read_only(C):
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_set_unencrypted_read_only(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+
+def test_unencrypted_volume_set_read_write(C):
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_set_unencrypted_read_write(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+
+def test_export_firmware(C):
+ assert C.NK_export_firmware(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
+
+
+def test_clear_new_sd_card_notification(C):
+ assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
+
+
+@pytest.mark.skip
+def test_fill_SD_card(C):
+ status = C.NK_fill_SD_card_with_random_data(DefaultPasswords.ADMIN)
+ assert status == DeviceErrorCode.STATUS_OK or status == DeviceErrorCode.BUSY
+ while 1:
+ value = C.NK_get_progress_bar_value()
+ if value == -1: break
+ assert 0 <= value <= 100
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
+ wait(5)
+
+
+def test_get_busy_progress_on_idle(C):
+ value = C.NK_get_progress_bar_value()
+ assert value == -1
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
+
+
+def test_change_update_password(C):
+ wrong_password = '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
+
+
+def test_send_startup(C):
+ time_seconds_from_epoch = 0 # FIXME set proper date
+ assert C.NK_send_startup(time_seconds_from_epoch) == DeviceErrorCode.STATUS_OK