aboutsummaryrefslogtreecommitdiff
path: root/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'unittest')
-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
9 files changed, 595 insertions, 170 deletions
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