From 130d7f12e42505a33f41073983d868ca0c3c78d1 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 8 Nov 2016 17:03:48 +0100 Subject: Fix for auth issue in NK Pro for commands EraseSlot, WriteToSlot, GetCode + tests Signed-off-by: Szczepan Zalega --- unittest/test3.cc | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 unittest/test3.cc (limited to 'unittest') diff --git a/unittest/test3.cc b/unittest/test3.cc new file mode 100644 index 0000000..6fab862 --- /dev/null +++ b/unittest/test3.cc @@ -0,0 +1,78 @@ +// +// Created by sz on 08.11.16. +// + +#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"; +const char * temporary_password = "123456789012345678901234"; +const char * RFC_SECRET = "12345678901234567890"; + +#include "catch.hpp" + +#include +#include +#include +#include "device_proto.h" +#include "log.h" +#include "stick10_commands.h" +#include "stick10_commands_0.8.h" +//#include "stick20_commands.h" + +using namespace std; +using namespace nitrokey::device; +using namespace nitrokey::proto; +//using namespace nitrokey::proto::stick10_08; +using namespace nitrokey::proto::stick10; +using namespace nitrokey::log; +using namespace nitrokey::misc; + +void connect_and_setup(Stick10 &stick) { + bool connected = stick.connect(); + REQUIRE(connected == true); + Log::instance().set_loglevel(Loglevel::DEBUG); +} + +void authorize(Stick10 &stick) { + auto authreq = get_payload(); + strcpy((char *) (authreq.card_password), default_admin_pin); + strcpy((char *) (authreq.temporary_password), temporary_password); + FirstAuthenticate::CommandTransaction::run(stick, authreq); +} + +TEST_CASE("write slot", "[pronew]"){ + Stick10 stick; + connect_and_setup(stick); + + auto p = get_payload(); +// p.slot_number = 0 + 0x10; + strcpyT(p.slot_secret, RFC_SECRET); + strcpyT(p.temporary_admin_password, temporary_password); + p.use_8_digits = true; + stick10_08::WriteToHOTPSlot::CommandTransaction::run(stick, p); + + auto p2 = get_payload(); + strcpyT(p2.temporary_admin_password, temporary_password); + p2.slot_number = 0 + 0x10; + p2.slot_counter = 0; + strcpyT(p2.slot_name, "test name aaa"); + stick10_08::WriteToHOTPSlot_2::CommandTransaction::run(stick, p2); + + auto p3 = get_payload(); + p3.slot_number = 0 + 0x10; + GetHOTP::CommandTransaction::run(stick, p3); + +} + + +TEST_CASE("erase slot", "[pronew]"){ + Stick10 stick; + connect_and_setup(stick); + authorize(stick); + + auto erase_payload = get_payload(); + erase_payload.slot_number = 1 + 0x10; + strcpyT(erase_payload.temporary_admin_password, temporary_password); + stick10_08::EraseSlot::CommandTransaction::run(stick, erase_payload); +} \ No newline at end of file -- cgit v1.2.3 From 119ea0b111c9ca46fda32911c1c8c33b36aad3db Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 8 Nov 2016 18:58:17 +0100 Subject: Authorization fix: GetHOTP and WriteGeneralConfig + test Signed-off-by: Szczepan Zalega --- command_id.cc | 3 ++ include/stick10_commands_0.8.h | 83 +++++++++++++++++++++++++++++++++++++++++- unittest/test3.cc | 69 ++++++++++++++++++++++++++++++++--- 3 files changed, 149 insertions(+), 6 deletions(-) (limited to 'unittest') diff --git a/command_id.cc b/command_id.cc index 9512b7d..a93d05c 100644 --- a/command_id.cc +++ b/command_id.cc @@ -139,6 +139,9 @@ const char *commandid_to_string(CommandID id) { return "DETECT_SC_AES"; case CommandID::NEW_AES_KEY: return "NEW_AES_KEY"; + case CommandID::WRITE_TO_SLOT_2: + return "WRITE_TO_SLOT_2"; + break; } return "UNKNOWN"; } diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index 037a777..0c6633c 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -12,6 +12,7 @@ #include "inttypes.h" #include "command.h" #include "device_proto.h" +#include "stick10_commands.h" namespace nitrokey { namespace proto { @@ -20,6 +21,8 @@ namespace nitrokey { * Stick10 protocol definition */ namespace stick10_08 { + using stick10::FirstAuthenticate; + using stick10::UserAuthenticate; class EraseSlot : Command { public: @@ -117,20 +120,68 @@ namespace nitrokey { CommandTransaction; }; + class GetHOTP : Command { + public: + struct CommandPayload { + uint8_t temporary_user_password[25]; + uint8_t slot_number; + + bool isValid() const { return (slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "temporary_user_password:\t" << temporary_user_password << std::endl; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + union { + uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1 + struct { + uint32_t code; + union{ + uint8_t _slot_config; + struct{ + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + } __packed; + } __packed; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "code:\t" << (code) << std::endl; + ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + class GetTOTP : Command { //user auth public: struct CommandPayload { + uint8_t temporary_user_password[25]; uint8_t slot_number; uint64_t challenge; uint64_t last_totp_time; uint8_t last_interval; - uint8_t user_temporary_password[25]; bool isValid() const { return !(slot_number & 0xF0); } std::string dissect() const { std::stringstream ss; + ss << "temporary_user_password:\t" << temporary_user_password << std::endl; ss << "slot_number:\t" << (int)(slot_number) << std::endl; ss << "challenge:\t" << (challenge) << std::endl; ss << "last_totp_time:\t" << (last_totp_time) << std::endl; @@ -172,6 +223,36 @@ namespace nitrokey { }; + class WriteGeneralConfig : Command { + //admin auth + public: + struct CommandPayload { + union{ + uint8_t config[5]; + struct{ + uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */ + uint8_t capslock; /** same as numlock */ + uint8_t scrolllock; /** same as numlock */ + uint8_t enable_user_password; + uint8_t delete_user_password; + }; + }; + uint8_t temporary_admin_password[25]; + + std::string dissect() const { + std::stringstream ss; + ss << "numlock:\t" << (int)numlock << std::endl; + ss << "capslock:\t" << (int)capslock << std::endl; + ss << "scrolllock:\t" << (int)scrolllock << std::endl; + ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl; + ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; } } } diff --git a/unittest/test3.cc b/unittest/test3.cc index 6fab862..226e35c 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -16,15 +16,13 @@ const char * RFC_SECRET = "12345678901234567890"; #include #include "device_proto.h" #include "log.h" -#include "stick10_commands.h" #include "stick10_commands_0.8.h" //#include "stick20_commands.h" using namespace std; using namespace nitrokey::device; using namespace nitrokey::proto; -//using namespace nitrokey::proto::stick10_08; -using namespace nitrokey::proto::stick10; +using namespace nitrokey::proto::stick10_08; using namespace nitrokey::log; using namespace nitrokey::misc; @@ -39,6 +37,11 @@ void authorize(Stick10 &stick) { strcpy((char *) (authreq.card_password), default_admin_pin); strcpy((char *) (authreq.temporary_password), temporary_password); FirstAuthenticate::CommandTransaction::run(stick, authreq); + + auto user_auth = get_payload(); + strcpyT(user_auth.temporary_password, temporary_password); + strcpyT(user_auth.card_password, default_user_pin); + UserAuthenticate::CommandTransaction::run(stick, user_auth); } TEST_CASE("write slot", "[pronew]"){ @@ -46,7 +49,6 @@ TEST_CASE("write slot", "[pronew]"){ connect_and_setup(stick); auto p = get_payload(); -// p.slot_number = 0 + 0x10; strcpyT(p.slot_secret, RFC_SECRET); strcpyT(p.temporary_admin_password, temporary_password); p.use_8_digits = true; @@ -71,8 +73,65 @@ TEST_CASE("erase slot", "[pronew]"){ connect_and_setup(stick); authorize(stick); + auto p3 = get_payload(); + p3.slot_number = 0 + 0x10; + GetHOTP::CommandTransaction::run(stick, p3); + auto erase_payload = get_payload(); - erase_payload.slot_number = 1 + 0x10; + erase_payload.slot_number = 0 + 0x10; strcpyT(erase_payload.temporary_admin_password, temporary_password); stick10_08::EraseSlot::CommandTransaction::run(stick, erase_payload); + + auto p4 = get_payload(); + p4.slot_number = 0 + 0x10; + REQUIRE_THROWS( + GetHOTP::CommandTransaction::run(stick, p4) + ); +} + +TEST_CASE("write general config", "[pronew]") { + Stick10 stick; + connect_and_setup(stick); + authorize(stick); + + auto p = get_payload(); + p.enable_user_password = 1; + REQUIRE_THROWS( + WriteGeneralConfig::CommandTransaction::run(stick, p); + ); + strcpyT(p.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, p); +} + +TEST_CASE("authorize user OTP", "[pronew]") { + Stick10 stick; + connect_and_setup(stick); + authorize(stick); + + auto p = get_payload(); + p.enable_user_password = 1; + strcpyT(p.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, p); + + auto pw = get_payload(); + strcpyT(pw.slot_secret, RFC_SECRET); + strcpyT(pw.temporary_admin_password, temporary_password); + pw.use_8_digits = true; + WriteToHOTPSlot::CommandTransaction::run(stick, pw); + + auto pw2 = get_payload(); + strcpyT(pw2.temporary_admin_password, temporary_password); + pw2.slot_number = 0 + 0x10; + pw2.slot_counter = 0; + strcpyT(pw2.slot_name, "test name aaa"); + WriteToHOTPSlot_2::CommandTransaction::run(stick, pw2); + + auto p3 = get_payload(); + p3.slot_number = 0 + 0x10; + REQUIRE_THROWS( + GetHOTP::CommandTransaction::run(stick, p3); + ); + strcpyT(p3.temporary_user_password, temporary_password); + GetHOTP::CommandTransaction::run(stick, p3); + } \ No newline at end of file -- cgit v1.2.3 From f76ac655fff3df7eb0e645ca39d18510714b0039 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 9 Nov 2016 14:29:18 +0100 Subject: Authorization fix: GetTOTP and WriteToTOTPSLot + test Signed-off-by: Szczepan Zalega --- include/stick10_commands_0.8.h | 77 ++++++++++++++++++++++++++++++++++++++++++ unittest/test3.cc | 46 +++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 2 deletions(-) (limited to 'unittest') diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index 0c6633c..cee0d78 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -23,6 +23,7 @@ namespace nitrokey { namespace stick10_08 { using stick10::FirstAuthenticate; using stick10::UserAuthenticate; + using stick10::SetTime; class EraseSlot : Command { public: @@ -120,6 +121,82 @@ namespace nitrokey { CommandTransaction; }; + + class WriteToTOTPSlot : Command { + //admin auth + public: + struct CommandPayload { + uint8_t temporary_admin_password[25]; + uint8_t slot_secret[20]; + union { + uint8_t _slot_config; + struct { + bool use_8_digits : 1; + bool use_enter : 1; + bool use_tokenID : 1; + }; + }; + union { + uint8_t slot_token_id[13]; /** OATH Token Identifier */ + struct { /** @see https://openauthentication.org/token-specs/ */ + uint8_t omp[2]; + uint8_t tt[2]; + uint8_t mui[8]; + uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 + } slot_token_fields; + }; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; + ss << "slot_secret:" << std::endl + << ::nitrokey::misc::hexdump((const char *) (&slot_secret), sizeof slot_secret); + ss << "slot_config:\t" << std::bitset<8>((int) _slot_config) << std::endl; + ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; + ss << "\tuse_enter(1):\t" << use_enter << std::endl; + ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + + ss << "slot_token_id:\t"; + for (auto i : slot_token_id) + ss << std::hex << std::setw(2) << std::setfill('0') << (int) i << " "; + ss << std::endl; + + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + class WriteToTOTPSlot_2 : Command { + public: + struct CommandPayload { + uint8_t temporary_admin_password[25]; + uint8_t slot_number; + uint8_t slot_name[15]; + uint16_t slot_interval; + + bool isValid() const { return !(slot_number & 0xF0); } + + std::string dissect() const { + std::stringstream ss; + ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; + ss << "slot_number:\t" << (int) (slot_number) << std::endl; + ss << "slot_name:\t" << slot_name << std::endl; + ss << "slot_interval:\t" << (int)slot_interval << std::endl; + + return ss.str(); + } + } __packed; + + typedef Transaction + CommandTransaction; + }; + + class GetHOTP : Command { public: struct CommandPayload { diff --git a/unittest/test3.cc b/unittest/test3.cc index 226e35c..8a9423f 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -103,7 +103,7 @@ TEST_CASE("write general config", "[pronew]") { WriteGeneralConfig::CommandTransaction::run(stick, p); } -TEST_CASE("authorize user OTP", "[pronew]") { +TEST_CASE("authorize user HOTP", "[pronew]") { Stick10 stick; connect_and_setup(stick); authorize(stick); @@ -132,6 +132,48 @@ TEST_CASE("authorize user OTP", "[pronew]") { GetHOTP::CommandTransaction::run(stick, p3); ); strcpyT(p3.temporary_user_password, temporary_password); - GetHOTP::CommandTransaction::run(stick, p3); + auto code_response = GetHOTP::CommandTransaction::run(stick, p3); + REQUIRE(code_response.data().code == 1284755224); + +} + + +TEST_CASE("authorize user TOTP", "[pronew]") { + Stick10 stick; + connect_and_setup(stick); + authorize(stick); + + auto p = get_payload(); + p.enable_user_password = 1; + strcpyT(p.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, p); + + auto pw = get_payload(); + strcpyT(pw.slot_secret, RFC_SECRET); + strcpyT(pw.temporary_admin_password, temporary_password); + pw.use_8_digits = true; + WriteToTOTPSlot::CommandTransaction::run(stick, pw); + + auto pw2 = get_payload(); + strcpyT(pw2.temporary_admin_password, temporary_password); + pw2.slot_number = 0 + 0x20; + pw2.slot_interval= 30; + strcpyT(pw2.slot_name, "test name TOTP"); + WriteToTOTPSlot_2::CommandTransaction::run(stick, pw2); + + auto p_get_totp = get_payload(); + p_get_totp.slot_number = 0 + 0x20; + + REQUIRE_THROWS( + GetTOTP::CommandTransaction::run(stick, p_get_totp); + ); + strcpyT(p_get_totp.temporary_user_password, temporary_password); + + auto p_set_time = get_payload(); + p_set_time.reset = 1; + p_set_time.time = 59; + SetTime::CommandTransaction::run(stick, p_set_time); + auto code = GetTOTP::CommandTransaction::run(stick, p_get_totp); + REQUIRE(code.data().code == 94287082); } \ No newline at end of file -- cgit v1.2.3 From e647b0cf5def2c76958968ddad8f7808d966aa49 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 9 Nov 2016 14:55:48 +0100 Subject: Tests - ensure required env is set, fix HOTP const Signed-off-by: Szczepan Zalega --- unittest/test3.cc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'unittest') diff --git a/unittest/test3.cc b/unittest/test3.cc index 8a9423f..6395cb6 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -48,19 +48,24 @@ TEST_CASE("write slot", "[pronew]"){ Stick10 stick; connect_and_setup(stick); - auto p = get_payload(); + auto p = get_payload(); strcpyT(p.slot_secret, RFC_SECRET); strcpyT(p.temporary_admin_password, temporary_password); p.use_8_digits = true; stick10_08::WriteToHOTPSlot::CommandTransaction::run(stick, p); - auto p2 = get_payload(); + auto p2 = get_payload(); strcpyT(p2.temporary_admin_password, temporary_password); p2.slot_number = 0 + 0x10; p2.slot_counter = 0; strcpyT(p2.slot_name, "test name aaa"); stick10_08::WriteToHOTPSlot_2::CommandTransaction::run(stick, p2); + auto pc = get_payload(); + pc.enable_user_password = 0; + strcpyT(pc.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, pc); + auto p3 = get_payload(); p3.slot_number = 0 + 0x10; GetHOTP::CommandTransaction::run(stick, p3); @@ -73,14 +78,19 @@ TEST_CASE("erase slot", "[pronew]"){ connect_and_setup(stick); authorize(stick); + auto p = get_payload(); + p.enable_user_password = 0; + strcpyT(p.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, p); + auto p3 = get_payload(); p3.slot_number = 0 + 0x10; GetHOTP::CommandTransaction::run(stick, p3); - auto erase_payload = get_payload(); + auto erase_payload = get_payload(); erase_payload.slot_number = 0 + 0x10; strcpyT(erase_payload.temporary_admin_password, temporary_password); - stick10_08::EraseSlot::CommandTransaction::run(stick, erase_payload); + EraseSlot::CommandTransaction::run(stick, erase_payload); auto p4 = get_payload(); p4.slot_number = 0 + 0x10; @@ -133,7 +143,7 @@ TEST_CASE("authorize user HOTP", "[pronew]") { ); strcpyT(p3.temporary_user_password, temporary_password); auto code_response = GetHOTP::CommandTransaction::run(stick, p3); - REQUIRE(code_response.data().code == 1284755224); + REQUIRE(code_response.data().code == 84755224); } -- cgit v1.2.3 From 17a1961704791c61fd69889867b3285c37539b59 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 9 Nov 2016 18:25:36 +0100 Subject: Authorize before write in WriteToSlot test Signed-off-by: Szczepan Zalega --- unittest/test3.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'unittest') diff --git a/unittest/test3.cc b/unittest/test3.cc index 6395cb6..bd6f2a5 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -47,6 +47,7 @@ void authorize(Stick10 &stick) { TEST_CASE("write slot", "[pronew]"){ Stick10 stick; connect_and_setup(stick); + authorize(stick); auto p = get_payload(); strcpyT(p.slot_secret, RFC_SECRET); -- cgit v1.2.3 From 90bf7f564c50bf48799056179dbc5a09b7782d27 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 9 Nov 2016 18:30:07 +0100 Subject: Check firware version in Pro 0.8 test Signed-off-by: Szczepan Zalega --- include/stick10_commands_0.8.h | 1 + unittest/test3.cc | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'unittest') diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index cee0d78..9099b3d 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -24,6 +24,7 @@ namespace nitrokey { using stick10::FirstAuthenticate; using stick10::UserAuthenticate; using stick10::SetTime; + using stick10::GetStatus; class EraseSlot : Command { public: diff --git a/unittest/test3.cc b/unittest/test3.cc index bd6f2a5..7f779a5 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -148,6 +148,13 @@ TEST_CASE("authorize user HOTP", "[pronew]") { } +TEST_CASE("check firmware version", "[pronew]") { + Stick10 stick; + connect_and_setup(stick); + + auto p = GetStatus::CommandTransaction::run(stick); + REQUIRE(p.data().firmware_version == 8); +} TEST_CASE("authorize user TOTP", "[pronew]") { Stick10 stick; -- cgit v1.2.3 From b94d61b2f3c446c46ac2f660d954841d740782f5 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 9 Nov 2016 19:20:51 +0100 Subject: Detect Pro 0.8 Signed-off-by: Szczepan Zalega --- unittest/misc.py | 4 ++++ unittest/test_pro.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'unittest') diff --git a/unittest/misc.py b/unittest/misc.py index b45436d..8296814 100644 --- a/unittest/misc.py +++ b/unittest/misc.py @@ -31,6 +31,10 @@ def is_pro_rtm_07(C): firmware = get_firmware_version_from_status(C) return '07 00' in firmware +def is_pro_rtm_08(C): + firmware = get_firmware_version_from_status(C) + return '08 00' in firmware + def is_storage(C): """ diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 6ab2af9..c7772d6 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -2,7 +2,7 @@ import pytest 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 +from misc import is_pro_rtm_07, is_pro_rtm_08, is_storage def test_enable_password_safe(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK @@ -61,7 +61,7 @@ def test_password_safe_slot_status(C): def test_issue_device_locks_on_second_key_generation_in_sequence(C): - if is_pro_rtm_07(C): + if is_pro_rtm_07(C) or is_pro_rtm_08(C): pytest.skip("issue to register: device locks up " "after below commands sequence (reinsertion fixes), skipping for now") assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK -- cgit v1.2.3 From acd426f8634678da15fc30f761f03e6520614fe0 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Thu, 10 Nov 2016 18:19:50 +0100 Subject: Add requirements for running pytest tests Signed-off-by: Szczepan Zalega --- unittest/requirements.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 unittest/requirements.txt (limited to 'unittest') diff --git a/unittest/requirements.txt b/unittest/requirements.txt new file mode 100644 index 0000000..7224741 --- /dev/null +++ b/unittest/requirements.txt @@ -0,0 +1,4 @@ +cffi +pytest-repeat +pytest-randomly +enum -- cgit v1.2.3 From d28794ef7d6d6fa7e8d6478fb912302691fe75e7 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 12 Nov 2016 18:34:23 +0100 Subject: Add test for STATUS_AES_DEC_FAILED error Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index c7772d6..5c8ecb4 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -74,6 +74,14 @@ def test_regenerate_aes_key(C): assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK +def test_enable_password_safe_after_factory_reset(C): + assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK + assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK + wait(10) + assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_AES_DEC_FAILED + assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK + assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK + @pytest.mark.xfail(reason="NK Pro firmware bug: regenerating AES key command not always results in cleared slot data") def test_destroy_password_safe(C): @@ -96,6 +104,7 @@ def test_destroy_password_safe(C): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK + assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert gs(C.NK_get_password_safe_slot_name(0)) != 'slotname1' -- cgit v1.2.3 From 411921354998d01229ba1cd10424df05441825be Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 15 Nov 2016 20:17:02 +0100 Subject: Tests: secret started with null byte Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 5c8ecb4..8c99f81 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -74,6 +74,7 @@ def test_regenerate_aes_key(C): assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK + def test_enable_password_safe_after_factory_reset(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK @@ -294,6 +295,7 @@ def test_HOTP_64bit_counter(C): assert C.NK_write_hotp_slot(slot_number, 'python_test', RFC_SECRET, t, use_8_digits, False, False, "", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code_device = str(C.NK_get_hotp_code(slot_number)) + code_device = '0'+code_device if len(code_device) < 6 else code_device dev_res += (t, code_device) lib_res += (t, lib_at(t)) assert dev_res == lib_res @@ -319,6 +321,7 @@ def test_TOTP_64bit_time(C): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_totp_set_time(t) == DeviceErrorCode.STATUS_OK code_device = str((C.NK_get_totp_code(slot_number, T, 0, 30))) + code_device = '0'+code_device if len(code_device) < 6 else code_device dev_res += (t, code_device) lib_res += (t, lib_at(t)) assert dev_res == lib_res @@ -495,3 +498,25 @@ def test_get_serial_number(C): sn = gs(sn) assert len(sn) > 0 print(('Serial number of the device: ', sn)) + +@pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ]) +def test_OTP_secret_started_from_null(C, secret): + oath = pytest.importorskip("oath") + lib_at = lambda t: oath.hotp(secret, t, format='dec6') + PIN_protection = False + use_8_digits = False + slot_number = 1 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + dev_res = [] + lib_res = [] + for t in range(1,5): + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_hotp_slot(slot_number, 'null_secret', secret, t, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + code_device = str(C.NK_get_hotp_code(slot_number)) + code_device = '0'+code_device if len(code_device) < 6 else code_device + dev_res += (t, code_device) + lib_res += (t, lib_at(t)) + assert dev_res == lib_res -- cgit v1.2.3 From 54d3b649b4c6d51120c16c64b7f824c81123a807 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 16 Nov 2016 11:25:17 +0100 Subject: Tests: more OTP, test all slots for read/write Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 8c99f81..a9e9fa4 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -520,3 +520,57 @@ def test_OTP_secret_started_from_null(C, secret): dev_res += (t, code_device) lib_res += (t, lib_at(t)) assert dev_res == lib_res + + +@pytest.mark.parametrize("counter", [0, 3, 7, 0xffff] ) +def test_HOTP_slots_read_write_counter(C, counter): + secret = RFC_SECRET + oath = pytest.importorskip("oath") + lib_at = lambda t: oath.hotp(secret, t, format='dec6') + PIN_protection = False + use_8_digits = False + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + dev_res = [] + lib_res = [] + for slot_number in range(3): + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_hotp_slot(slot_number, 'null_secret', secret, counter, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + code_device = str(C.NK_get_hotp_code(slot_number)) + code_device = '0'+code_device if len(code_device) < 6 else code_device + dev_res += (counter, code_device) + lib_res += (counter, lib_at(counter)) + assert dev_res == lib_res + + +@pytest.mark.parametrize("period", [30,60] ) +@pytest.mark.parametrize("time", range(20,70,20) ) +def test_TOTP_slots_read_write_at_time_period(C, time, period): + secret = RFC_SECRET + oath = pytest.importorskip("oath") + lib_at = lambda t: oath.totp(RFC_SECRET, t=t, period=period) + PIN_protection = False + use_8_digits = False + T = 0 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + dev_res = [] + lib_res = [] + for slot_number in range(15): + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_totp_slot(slot_number, 'null_secret', secret, period, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK + code_device = str(C.NK_get_totp_code(slot_number, T, 0, period)) + code_device = '0'+code_device if len(code_device) < 6 else code_device + dev_res += (time, code_device) + lib_res += (time, lib_at(time)) + assert dev_res == lib_res + + + + -- cgit v1.2.3 From cbccc871329c5522449010ae5007278123508820 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Wed, 16 Nov 2016 18:32:38 +0100 Subject: Use another OTP writing protocol and test it Signed-off-by: Szczepan Zalega --- NitrokeyManager.cc | 67 +++++++++++++++---------- command_id.cc | 3 ++ include/command_id.h | 1 + include/stick10_commands_0.8.h | 111 +++++++++-------------------------------- unittest/constants.py | 2 +- unittest/test3.cc | 111 ++++++++++++++++++++++++++--------------- unittest/test_pro.py | 33 ++++++++++-- 7 files changed, 169 insertions(+), 159 deletions(-) (limited to 'unittest') diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index e80f9b5..46c09df 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -228,23 +228,30 @@ namespace nitrokey{ uint64_t hotp_counter, bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID, const char *temporary_password) const { - auto payload = get_payload(); - strcpyT(payload.temporary_admin_password, temporary_password); + auto payload2 = get_payload(); + strcpyT(payload2.temporary_admin_password, temporary_password); + strcpyT(payload2.data, slot_name); + payload2.length = strlen((const char *) payload2.data); + payload2.setTypeName(); + stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); + + payload2 = get_payload(); + strcpyT(payload2.temporary_admin_password, temporary_password); auto secret_bin = misc::hex_string_to_byte(secret); - vector_copy(payload.slot_secret, secret_bin); + vector_copy(payload2.data, secret_bin); + payload2.length = strlen((const char *) payload2.data); + payload2.setTypeSecret(); + stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); + + auto payload = get_payload(); + strcpyT(payload.temporary_admin_password, temporary_password); strcpyT(payload.slot_token_id, token_ID); payload.use_8_digits = use_8_digits; payload.use_enter = use_enter; payload.use_tokenID = use_tokenID; - - auto payload2 = get_payload(); - strcpyT(payload2.temporary_admin_password, temporary_password); - payload2.slot_number = slot_number; - strcpyT(payload2.slot_name, slot_name); - payload2.slot_counter = hotp_counter; - - stick10_08::WriteToHOTPSlot::CommandTransaction::run(*device, payload); - stick10_08::WriteToHOTPSlot_2::CommandTransaction::run(*device, payload2); + payload.slot_counter_or_interval = hotp_counter; + payload.slot_number = slot_number; + stick10_08::WriteToOTPSlot::CommandTransaction::run(*device, payload); } void NitrokeyManager::write_HOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret, @@ -302,23 +309,31 @@ namespace nitrokey{ uint16_t time_window, bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID, const char *temporary_password) const { - auto payload = get_payload(); - strcpyT(payload.temporary_admin_password, temporary_password); + + auto payload2 = get_payload(); + strcpyT(payload2.temporary_admin_password, temporary_password); + strcpyT(payload2.data, slot_name); + payload2.length = strlen((const char *) payload2.data); + payload2.setTypeName(); + stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); + + payload2 = get_payload(); + strcpyT(payload2.temporary_admin_password, temporary_password); auto secret_bin = misc::hex_string_to_byte(secret); - vector_copy(payload.slot_secret, secret_bin); + vector_copy(payload2.data, secret_bin); + payload2.length = strlen((const char *) payload2.data); + payload2.setTypeSecret(); + stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); + + auto payload = get_payload(); + strcpyT(payload.temporary_admin_password, temporary_password); strcpyT(payload.slot_token_id, token_ID); payload.use_8_digits = use_8_digits; payload.use_enter = use_enter; payload.use_tokenID = use_tokenID; - - auto payload2 = get_payload(); - strcpyT(payload2.temporary_admin_password, temporary_password); - payload2.slot_number = slot_number; - strcpyT(payload2.slot_name, slot_name); - payload2.slot_interval= time_window; - - stick10_08::WriteToTOTPSlot::CommandTransaction::run(*device, payload); - stick10_08::WriteToTOTPSlot_2::CommandTransaction::run(*device, payload2); + payload.slot_counter_or_interval = time_window; + payload.slot_number = slot_number; + stick10_08::WriteToOTPSlot::CommandTransaction::run(*device, payload); } void NitrokeyManager::write_TOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret, @@ -583,8 +598,8 @@ namespace nitrokey{ bool NitrokeyManager::is_authorization_command_supported(){ auto m = std::unordered_map({ - {DeviceModel::PRO, 7}, - {DeviceModel::STORAGE, 43}, + {DeviceModel::PRO, 7}, + {DeviceModel::STORAGE, 43}, }); auto status_p = GetStatus::CommandTransaction::run(*device); return status_p.data().firmware_version <= m[device->get_device_model()]; diff --git a/command_id.cc b/command_id.cc index a93d05c..f76a358 100644 --- a/command_id.cc +++ b/command_id.cc @@ -142,6 +142,9 @@ const char *commandid_to_string(CommandID id) { case CommandID::WRITE_TO_SLOT_2: return "WRITE_TO_SLOT_2"; break; + case CommandID::SEND_OTP_DATA: + return "SEND_OTP_DATA"; + break; } return "UNKNOWN"; } diff --git a/include/command_id.h b/include/command_id.h index 1f7affc..346b750 100644 --- a/include/command_id.h +++ b/include/command_id.h @@ -66,6 +66,7 @@ enum class CommandID : uint8_t { CHANGE_USER_PIN = 0x14, CHANGE_ADMIN_PIN = 0x15, WRITE_TO_SLOT_2 = 0x16, + SEND_OTP_DATA = 0x17, ENABLE_CRYPTED_PARI = 0x20, DISABLE_CRYPTED_PARI = 0x20 + 1, //@unused diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index 3644c4d..e880c0a 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -44,47 +44,33 @@ namespace nitrokey { CommandTransaction; }; - class WriteToHOTPSlot : Command { + class SendOTPData : Command { //admin auth public: struct CommandPayload { uint8_t temporary_admin_password[25]; - uint8_t slot_secret[20]; - union { - uint8_t _slot_config; - struct { - bool use_8_digits : 1; - bool use_enter : 1; - bool use_tokenID : 1; - }; - }; - union { - uint8_t slot_token_id[13]; /** OATH Token Identifier */ - struct { /** @see https://openauthentication.org/token-specs/ */ - uint8_t omp[2]; - uint8_t tt[2]; - uint8_t mui[8]; - uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805 - } slot_token_fields; - }; + uint8_t type; //0-secret, 1-name + uint8_t id; //multiple reports for values longer than 30 bytes + uint8_t length; //data length + uint8_t data[30]; //data, does not need null termination bool isValid() const { return true; } + void setTypeName(){ + type = 'N'; + } + void setTypeSecret(){ + type = 'S'; + } + std::string dissect() const { std::stringstream ss; ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; - ss << "slot_secret:" << std::endl - << ::nitrokey::misc::hexdump((const char *) (&slot_secret), sizeof slot_secret); - ss << "slot_config:\t" << std::bitset<8>((int) _slot_config) << std::endl; - ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; - ss << "\tuse_enter(1):\t" << use_enter << std::endl; - ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; - - ss << "slot_token_id:\t"; - for (auto i : slot_token_id) - ss << std::hex << std::setw(2) << std::setfill('0') << (int) i << " "; - ss << std::endl; - + ss << "type:\t" << type << std::endl; + ss << "id:\t" << (int)id << std::endl; + ss << "length:\t" << (int)length << std::endl; + ss << "data:" << std::endl + << ::nitrokey::misc::hexdump((const char *) (&data), sizeof data); return ss.str(); } } __packed; @@ -93,42 +79,16 @@ namespace nitrokey { CommandTransaction; }; - class WriteToHOTPSlot_2 : Command { + class WriteToOTPSlot : Command { + //admin auth public: struct CommandPayload { uint8_t temporary_admin_password[25]; uint8_t slot_number; - uint8_t slot_name[15]; union { - uint64_t slot_counter; + uint64_t slot_counter_or_interval; uint8_t slot_counter_s[8]; } __packed; - - bool isValid() const { return !(slot_number & 0xF0); } - - std::string dissect() const { - std::stringstream ss; - ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; - ss << "slot_number:\t" << (int) (slot_number) << std::endl; - ss << "slot_name:\t" << slot_name << std::endl; - ss << "slot_counter:\t[" << (int) slot_counter << "]\t" - << ::nitrokey::misc::hexdump((const char *) (&slot_counter), sizeof slot_counter, false); - - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - - class WriteToTOTPSlot : Command { - //admin auth - public: - struct CommandPayload { - uint8_t temporary_admin_password[25]; - uint8_t slot_secret[20]; union { uint8_t _slot_config; struct { @@ -152,12 +112,13 @@ namespace nitrokey { std::string dissect() const { std::stringstream ss; ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; - ss << "slot_secret:" << std::endl - << ::nitrokey::misc::hexdump((const char *) (&slot_secret), sizeof slot_secret); ss << "slot_config:\t" << std::bitset<8>((int) _slot_config) << std::endl; ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl; ss << "\tuse_enter(1):\t" << use_enter << std::endl; ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl; + ss << "slot_number:\t" << (int) (slot_number) << std::endl; + ss << "slot_counter_or_interval:\t[" << (int) slot_counter_or_interval << "]\t" + << ::nitrokey::misc::hexdump((const char *) (&slot_counter_or_interval), sizeof slot_counter_or_interval, false); ss << "slot_token_id:\t"; for (auto i : slot_token_id) @@ -172,32 +133,6 @@ namespace nitrokey { CommandTransaction; }; - class WriteToTOTPSlot_2 : Command { - public: - struct CommandPayload { - uint8_t temporary_admin_password[25]; - uint8_t slot_number; - uint8_t slot_name[15]; - uint16_t slot_interval; - - bool isValid() const { return !(slot_number & 0xF0); } - - std::string dissect() const { - std::stringstream ss; - ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; - ss << "slot_number:\t" << (int) (slot_number) << std::endl; - ss << "slot_name:\t" << slot_name << std::endl; - ss << "slot_interval:\t" << (int)slot_interval << std::endl; - - return ss.str(); - } - } __packed; - - typedef Transaction - CommandTransaction; - }; - - class GetHOTP : Command { public: struct CommandPayload { diff --git a/unittest/constants.py b/unittest/constants.py index 78a219b..3c19a9b 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -2,7 +2,7 @@ from enum import Enum from misc import to_hex RFC_SECRET_HR = '12345678901234567890' -RFC_SECRET = to_hex(RFC_SECRET_HR) # '12345678901234567890' +RFC_SECRET = to_hex(RFC_SECRET_HR) # '3031323334353637383930...' # print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) ) diff --git a/unittest/test3.cc b/unittest/test3.cc index 7f779a5..7b37a60 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -49,18 +49,26 @@ TEST_CASE("write slot", "[pronew]"){ connect_and_setup(stick); authorize(stick); - auto p = get_payload(); - strcpyT(p.slot_secret, RFC_SECRET); - strcpyT(p.temporary_admin_password, temporary_password); - p.use_8_digits = true; - stick10_08::WriteToHOTPSlot::CommandTransaction::run(stick, p); + auto p2 = get_payload(); + strcpyT(p2.temporary_admin_password, temporary_password); + p2.setTypeName(); + strcpyT(p2.data, "test name aaa"); + p2.length = strlen((const char *) p2.data); + stick10_08::SendOTPData::CommandTransaction::run(stick, p2); - auto p2 = get_payload(); + p2 = get_payload(); strcpyT(p2.temporary_admin_password, temporary_password); - p2.slot_number = 0 + 0x10; - p2.slot_counter = 0; - strcpyT(p2.slot_name, "test name aaa"); - stick10_08::WriteToHOTPSlot_2::CommandTransaction::run(stick, p2); + strcpyT(p2.data, RFC_SECRET); + p2.length = strlen(RFC_SECRET); + p2.setTypeSecret(); + stick10_08::SendOTPData::CommandTransaction::run(stick, p2); + + auto p = get_payload(); + strcpyT(p.temporary_admin_password, temporary_password); + p.use_8_digits = true; + p.slot_number = 0 + 0x10; + p.slot_counter_or_interval = 0; + stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p); auto pc = get_payload(); pc.enable_user_password = 0; @@ -119,23 +127,34 @@ TEST_CASE("authorize user HOTP", "[pronew]") { connect_and_setup(stick); authorize(stick); - auto p = get_payload(); - p.enable_user_password = 1; - strcpyT(p.temporary_admin_password, temporary_password); - WriteGeneralConfig::CommandTransaction::run(stick, p); + { + auto p = get_payload(); + p.enable_user_password = 1; + strcpyT(p.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, p); + } + + auto p2 = get_payload(); + strcpyT(p2.temporary_admin_password, temporary_password); + p2.setTypeName(); + strcpyT(p2.data, "test name aaa"); + p2.length = strlen((const char *) p2.data); + stick10_08::SendOTPData::CommandTransaction::run(stick, p2); + + p2 = get_payload(); + strcpyT(p2.temporary_admin_password, temporary_password); + strcpyT(p2.data, RFC_SECRET); + p2.length = strlen(RFC_SECRET); + p2.setTypeSecret(); + stick10_08::SendOTPData::CommandTransaction::run(stick, p2); - auto pw = get_payload(); - strcpyT(pw.slot_secret, RFC_SECRET); - strcpyT(pw.temporary_admin_password, temporary_password); - pw.use_8_digits = true; - WriteToHOTPSlot::CommandTransaction::run(stick, pw); + auto p = get_payload(); + strcpyT(p.temporary_admin_password, temporary_password); + p.use_8_digits = true; + p.slot_number = 0 + 0x10; + p.slot_counter_or_interval = 0; + stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p); - auto pw2 = get_payload(); - strcpyT(pw2.temporary_admin_password, temporary_password); - pw2.slot_number = 0 + 0x10; - pw2.slot_counter = 0; - strcpyT(pw2.slot_name, "test name aaa"); - WriteToHOTPSlot_2::CommandTransaction::run(stick, pw2); auto p3 = get_payload(); p3.slot_number = 0 + 0x10; @@ -161,23 +180,33 @@ TEST_CASE("authorize user TOTP", "[pronew]") { connect_and_setup(stick); authorize(stick); - auto p = get_payload(); - p.enable_user_password = 1; - strcpyT(p.temporary_admin_password, temporary_password); - WriteGeneralConfig::CommandTransaction::run(stick, p); + { + auto p = get_payload(); + p.enable_user_password = 1; + strcpyT(p.temporary_admin_password, temporary_password); + WriteGeneralConfig::CommandTransaction::run(stick, p); + } - auto pw = get_payload(); - strcpyT(pw.slot_secret, RFC_SECRET); - strcpyT(pw.temporary_admin_password, temporary_password); - pw.use_8_digits = true; - WriteToTOTPSlot::CommandTransaction::run(stick, pw); - - auto pw2 = get_payload(); - strcpyT(pw2.temporary_admin_password, temporary_password); - pw2.slot_number = 0 + 0x20; - pw2.slot_interval= 30; - strcpyT(pw2.slot_name, "test name TOTP"); - WriteToTOTPSlot_2::CommandTransaction::run(stick, pw2); + auto p2 = get_payload(); + strcpyT(p2.temporary_admin_password, temporary_password); + p2.setTypeName(); + strcpyT(p2.data, "test name TOTP"); + p2.length = strlen((const char *) p2.data); + stick10_08::SendOTPData::CommandTransaction::run(stick, p2); + + p2 = get_payload(); + strcpyT(p2.temporary_admin_password, temporary_password); + strcpyT(p2.data, RFC_SECRET); + p2.length = strlen(RFC_SECRET); + p2.setTypeSecret(); + stick10_08::SendOTPData::CommandTransaction::run(stick, p2); + + auto p = get_payload(); + strcpyT(p.temporary_admin_password, temporary_password); + p.use_8_digits = true; + p.slot_number = 0 + 0x20; + p.slot_counter_or_interval = 30; + stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p); auto p_get_totp = get_payload(); p_get_totp.slot_number = 0 + 0x20; diff --git a/unittest/test_pro.py b/unittest/test_pro.py index a9e9fa4..3632ecd 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -536,7 +536,7 @@ def test_HOTP_slots_read_write_counter(C, counter): lib_res = [] for slot_number in range(3): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_hotp_slot(slot_number, 'null_secret', secret, counter, use_8_digits, False, False, "", + assert C.NK_write_hotp_slot(slot_number, 'HOTP rw' + str(slot_number), secret, counter, use_8_digits, False, False, "", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK code_device = str(C.NK_get_hotp_code(slot_number)) code_device = '0'+code_device if len(code_device) < 6 else code_device @@ -546,7 +546,7 @@ def test_HOTP_slots_read_write_counter(C, counter): @pytest.mark.parametrize("period", [30,60] ) -@pytest.mark.parametrize("time", range(20,70,20) ) +@pytest.mark.parametrize("time", range(21,70,20) ) def test_TOTP_slots_read_write_at_time_period(C, time, period): secret = RFC_SECRET oath = pytest.importorskip("oath") @@ -561,7 +561,7 @@ def test_TOTP_slots_read_write_at_time_period(C, time, period): lib_res = [] for slot_number in range(15): assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_totp_slot(slot_number, 'null_secret', secret, period, use_8_digits, False, False, "", + assert C.NK_write_totp_slot(slot_number, 'TOTP rw' + str(slot_number), secret, period, use_8_digits, False, False, "", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK @@ -572,5 +572,32 @@ def test_TOTP_slots_read_write_at_time_period(C, time, period): assert dev_res == lib_res +@pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET] ) +def test_TOTP_secrets(C, secret): + slot_number = 0 + time = 0 + period = 30 + oath = pytest.importorskip("oath") + lib_at = lambda t: oath.totp(secret, t=t, period=period) + PIN_protection = False + use_8_digits = False + T = 0 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + dev_res = [] + lib_res = [] + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_totp_slot(slot_number, 'TOTP secret' + str(slot_number), secret, period, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK + code_device = str(C.NK_get_totp_code(slot_number, T, 0, period)) + code_device = '0'+code_device if len(code_device) < 6 else code_device + dev_res += (time, code_device) + lib_res += (time, lib_at(time)) + assert dev_res == lib_res + + -- cgit v1.2.3 From 9c2feef240e396648dfb2378f7d2428b0593c9f2 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 18 Nov 2016 12:52:50 +0100 Subject: Support longer secrets (40 bytes) for NK Pro 0.8 Signed-off-by: Szczepan Zalega --- NitrokeyManager.cc | 29 +++++++++++++++++++++------ include/stick10_commands_0.8.h | 18 ++++++++++++++++- misc.cc | 5 +++-- unittest/test_pro.py | 45 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 80 insertions(+), 17 deletions(-) (limited to 'unittest') diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 46c09df..7cc88eb 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -198,6 +198,16 @@ namespace nitrokey{ return erase_slot(slot_number, temporary_password); } + template + void vector_copy_ranged(T& dest, std::vector &vec, size_t begin, size_t elements_to_copy){ + const size_t d_size = sizeof(dest); + if(d_size < elements_to_copy){ + throw TargetBufferSmallerThanSource(elements_to_copy, d_size); + } + std::fill(dest, dest+d_size, 0); + std::copy(vec.begin() + begin, vec.begin() +begin + elements_to_copy, dest); + } + template void vector_copy(T& dest, std::vector &vec){ const size_t d_size = sizeof(dest); @@ -317,13 +327,20 @@ namespace nitrokey{ payload2.setTypeName(); stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); - payload2 = get_payload(); - strcpyT(payload2.temporary_admin_password, temporary_password); - auto secret_bin = misc::hex_string_to_byte(secret); - vector_copy(payload2.data, secret_bin); - payload2.length = strlen((const char *) payload2.data); payload2.setTypeSecret(); - stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); + payload2.id = 0; + auto secret_bin = misc::hex_string_to_byte(secret); + auto remaining_secret_length = secret_bin.size(); + + while (remaining_secret_length>0){ + const auto bytesToCopy = std::min(sizeof(payload2.data), remaining_secret_length); + const auto start = secret_bin.size() - remaining_secret_length; + memset(payload2.data, 0, sizeof(payload2.data)); + vector_copy_ranged(payload2.data, secret_bin, start, bytesToCopy); + stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); + remaining_secret_length -= bytesToCopy; + payload2.id++; + } auto payload = get_payload(); strcpyT(payload.temporary_admin_password, temporary_password); diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index e880c0a..f0e28a6 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -75,7 +75,23 @@ namespace nitrokey { } } __packed; - typedef Transaction + + struct ResponsePayload { + union { + uint8_t data[40]; + } __packed; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "data:" << std::endl + << ::nitrokey::misc::hexdump((const char *) (&data), sizeof data); + return ss.str(); + } + } __packed; + + + typedef Transaction CommandTransaction; }; diff --git a/misc.cc b/misc.cc index c9d38cb..7a3c199 100644 --- a/misc.cc +++ b/misc.cc @@ -16,7 +16,8 @@ std::vector hex_string_to_byte(const char* hexString){ if (s_size%2!=0 || s_size==0 || s_size>big_string_size){ throw InvalidHexString(0); } - auto data = std::vector(d_size, 0); + auto data = std::vector(); + data.reserve(d_size); char buf[2]; for(int i=0; i hex_string_to_byte(const char* hexString){ } buf[i%2] = c; if (i%2==1){ - data[i/2] = strtoul(buf, NULL, 16) & 0xFF; + data.push_back( strtoul(buf, NULL, 16) & 0xFF ); } } return data; diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 3632ecd..71abfba 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -501,6 +501,9 @@ def test_get_serial_number(C): @pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ]) def test_OTP_secret_started_from_null(C, secret): + ''' + NK Pro 0.8+, NK Storage 0.43+ + ''' oath = pytest.importorskip("oath") lib_at = lambda t: oath.hotp(secret, t, format='dec6') PIN_protection = False @@ -522,7 +525,7 @@ def test_OTP_secret_started_from_null(C, secret): assert dev_res == lib_res -@pytest.mark.parametrize("counter", [0, 3, 7, 0xffff] ) +@pytest.mark.parametrize("counter", [0, 3, 7, 0xffff, 0xffffffff, 0xffffffffffffffff] ) def test_HOTP_slots_read_write_counter(C, counter): secret = RFC_SECRET oath = pytest.importorskip("oath") @@ -571,9 +574,13 @@ def test_TOTP_slots_read_write_at_time_period(C, time, period): lib_res += (time, lib_at(time)) assert dev_res == lib_res - -@pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET] ) +@pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] ) def test_TOTP_secrets(C, secret): + ''' + NK Pro 0.8+, NK Storage 0.44+ + ''' + if is_pro_rtm_07(C) and len(secret)>20*2: #*2 since secret is in hex + pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 ") slot_number = 0 time = 0 period = 30 @@ -587,10 +594,8 @@ def test_TOTP_secrets(C, secret): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK dev_res = [] lib_res = [] - assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_write_totp_slot(slot_number, 'TOTP secret' + str(slot_number), secret, period, use_8_digits, False, False, "", + assert C.NK_write_totp_slot(slot_number, 'secret' + str(len(secret)), secret, period, use_8_digits, False, False, "", DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK code_device = str(C.NK_get_totp_code(slot_number, T, 0, period)) code_device = '0'+code_device if len(code_device) < 6 else code_device @@ -598,6 +603,30 @@ def test_TOTP_secrets(C, secret): lib_res += (time, lib_at(time)) assert dev_res == lib_res - - +@pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] ) +def test_HOTP_secrets(C, secret): + ''' + NK Pro 0.8+, NK Storage 0.44+ + ''' + if is_pro_rtm_07(C) and len(secret)>20*2: #*2 since secret is in hex + pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 ") + slot_number = 0 + counter = 0 + oath = pytest.importorskip("oath") + lib_at = lambda t: oath.hotp(secret, counter=t) + PIN_protection = False + use_8_digits = False + T = 0 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + dev_res = [] + lib_res = [] + assert C.NK_write_hotp_slot(slot_number, 'secret' + str(len(secret)), secret, counter, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + code_device = str(C.NK_get_hotp_code(slot_number)) + code_device = '0'+code_device if len(code_device) < 6 else code_device + dev_res += (counter, code_device) + lib_res += (counter, lib_at(counter)) + assert dev_res == lib_res -- cgit v1.2.3 From f615000166177dad7128247d5c99679d9560c510 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 19 Nov 2016 14:09:40 +0100 Subject: Remove length field from send_otp_data packet Signed-off-by: Szczepan Zalega --- NitrokeyManager.cc | 1 - include/stick10_commands_0.8.h | 2 -- unittest/test3.cc | 6 ------ 3 files changed, 9 deletions(-) (limited to 'unittest') diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 7cc88eb..b991af1 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -323,7 +323,6 @@ namespace nitrokey{ auto payload2 = get_payload(); strcpyT(payload2.temporary_admin_password, temporary_password); strcpyT(payload2.data, slot_name); - payload2.length = strlen((const char *) payload2.data); payload2.setTypeName(); stick10_08::SendOTPData::CommandTransaction::run(*device, payload2); diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index f0e28a6..f905794 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -51,7 +51,6 @@ namespace nitrokey { uint8_t temporary_admin_password[25]; uint8_t type; //0-secret, 1-name uint8_t id; //multiple reports for values longer than 30 bytes - uint8_t length; //data length uint8_t data[30]; //data, does not need null termination bool isValid() const { return true; } @@ -68,7 +67,6 @@ namespace nitrokey { ss << "temporary_admin_password:\t" << temporary_admin_password << std::endl; ss << "type:\t" << type << std::endl; ss << "id:\t" << (int)id << std::endl; - ss << "length:\t" << (int)length << std::endl; ss << "data:" << std::endl << ::nitrokey::misc::hexdump((const char *) (&data), sizeof data); return ss.str(); diff --git a/unittest/test3.cc b/unittest/test3.cc index 7b37a60..9049365 100644 --- a/unittest/test3.cc +++ b/unittest/test3.cc @@ -53,13 +53,11 @@ TEST_CASE("write slot", "[pronew]"){ strcpyT(p2.temporary_admin_password, temporary_password); p2.setTypeName(); strcpyT(p2.data, "test name aaa"); - p2.length = strlen((const char *) p2.data); stick10_08::SendOTPData::CommandTransaction::run(stick, p2); p2 = get_payload(); strcpyT(p2.temporary_admin_password, temporary_password); strcpyT(p2.data, RFC_SECRET); - p2.length = strlen(RFC_SECRET); p2.setTypeSecret(); stick10_08::SendOTPData::CommandTransaction::run(stick, p2); @@ -138,13 +136,11 @@ TEST_CASE("authorize user HOTP", "[pronew]") { strcpyT(p2.temporary_admin_password, temporary_password); p2.setTypeName(); strcpyT(p2.data, "test name aaa"); - p2.length = strlen((const char *) p2.data); stick10_08::SendOTPData::CommandTransaction::run(stick, p2); p2 = get_payload(); strcpyT(p2.temporary_admin_password, temporary_password); strcpyT(p2.data, RFC_SECRET); - p2.length = strlen(RFC_SECRET); p2.setTypeSecret(); stick10_08::SendOTPData::CommandTransaction::run(stick, p2); @@ -191,13 +187,11 @@ TEST_CASE("authorize user TOTP", "[pronew]") { strcpyT(p2.temporary_admin_password, temporary_password); p2.setTypeName(); strcpyT(p2.data, "test name TOTP"); - p2.length = strlen((const char *) p2.data); stick10_08::SendOTPData::CommandTransaction::run(stick, p2); p2 = get_payload(); strcpyT(p2.temporary_admin_password, temporary_password); strcpyT(p2.data, RFC_SECRET); - p2.length = strlen(RFC_SECRET); p2.setTypeSecret(); stick10_08::SendOTPData::CommandTransaction::run(stick, p2); -- cgit v1.2.3 From b33681083348588caa3db3885c811a3c42d5b094 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 19 Nov 2016 14:14:43 +0100 Subject: Support sending empty OTP secrets for slot edit (+test) Signed-off-by: Szczepan Zalega --- misc.cc | 2 +- unittest/test_pro.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/misc.cc b/misc.cc index 7a3c199..3f15520 100644 --- a/misc.cc +++ b/misc.cc @@ -13,7 +13,7 @@ std::vector hex_string_to_byte(const char* hexString){ const size_t big_string_size = 256; //arbitrary 'big' number const size_t s_size = strlen(hexString); const size_t d_size = s_size/2; - if (s_size%2!=0 || s_size==0 || s_size>big_string_size){ + if (s_size%2!=0 || s_size>big_string_size){ throw InvalidHexString(0); } auto data = std::vector(); diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 71abfba..b8109f2 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -630,3 +630,34 @@ def test_HOTP_secrets(C, secret): lib_res += (counter, lib_at(counter)) assert dev_res == lib_res + +def test_edit_OTP_slot(C): + """ + should change slots counter and name without changing its secret (using null secret for second update) + """ + secret = RFC_SECRET + counter = 0 + PIN_protection = False + use_8_digits = False + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + slot_number = 0 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + first_name = 'edit slot' + assert C.NK_write_hotp_slot(slot_number, first_name, secret, counter, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert gs(C.NK_get_hotp_slot_name(slot_number)) == first_name + + + first_code = C.NK_get_hotp_code(slot_number) + changed_name = 'changedname' + empty_secret = '' + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_hotp_slot(slot_number, changed_name, empty_secret, counter, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + second_code = C.NK_get_hotp_code(slot_number) + assert first_code == second_code + assert gs(C.NK_get_hotp_slot_name(slot_number)) == changed_name + + -- cgit v1.2.3 From fbe8f668eb3ceb02a23f943d5db5070b0cafc401 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 19 Nov 2016 14:16:44 +0100 Subject: Test to configure getting HOTP codes through special key double press Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index b8109f2..aaf884f 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -631,6 +631,25 @@ def test_HOTP_secrets(C, secret): assert dev_res == lib_res +def test_special_double_press(C): + """ + requires manual check after function run + double press each of num-, scroll-, caps-lock and check inserted OTP codes (each 1st should be 755224) + on nkpro 0.7 scrolllock should do nothing, on nkpro 0.8+ should return OTP code + """ + secret = RFC_SECRET + counter = 0 + PIN_protection = False + use_8_digits = False + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(0, 1, 2, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + for slot_number in range(3): + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_hotp_slot(slot_number, 'double' + str(slot_number), secret, counter, use_8_digits, False, False, "", + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + # requires manual check + def test_edit_OTP_slot(C): """ should change slots counter and name without changing its secret (using null secret for second update) -- cgit v1.2.3 From 5adc4b754de0a55f8c92dfbcd868630e65b4781f Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 19 Nov 2016 14:36:02 +0100 Subject: Update description for TOTP_RFC_usepin test Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index aaf884f..c8be0e8 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -327,9 +327,12 @@ def test_TOTP_64bit_time(C): assert dev_res == lib_res -@pytest.mark.xfail(reason="NK Pro: possible firmware bug or communication issue: set time command not always changes the time on stick thus failing this test, " - "this does not influence normal use since setting time is not done every TOTP code request" - "Rarely fail occurs on NK Storage") +@pytest.mark.xfail(reason="NK Pro: Test fails in 50% of cases due to test vectors set 1 second before interval count change" + "Here time is changed on seconds side only and miliseconds part is not being reset apparently" + "This results in available time to test of half a second on average, thus 50% failed cases" + "With disabled two first test vectors test passess 10/10 times" + "Fail may also occurs on NK Storage with lower occurrency since it needs less time to execute " + "commands") @pytest.mark.parametrize("PIN_protection", [False, True, ]) def test_TOTP_RFC_usepin(C, PIN_protection): slot_number = 1 @@ -350,8 +353,8 @@ def test_TOTP_RFC_usepin(C, PIN_protection): # Mode: Sha1, time step X=30 test_data = [ #Time T (hex) TOTP - (59, 0x1, 94287082), - (1111111109, 0x00000000023523EC, 7081804), + (59, 0x1, 94287082), # Warning - test vector time 1 second before interval count changes + (1111111109, 0x00000000023523EC, 7081804), # Warning - test vector time 1 second before interval count changes (1111111111, 0x00000000023523ED, 14050471), (1234567890, 0x000000000273EF07, 89005924), (2000000000, 0x0000000003F940AA, 69279037), -- cgit v1.2.3 From 9e0c85241234ae607c7eea4bb9f3ee61762b5c0c Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 26 Nov 2016 15:40:46 +0100 Subject: Update comments Signed-off-by: Szczepan Zalega --- include/stick10_commands_0.8.h | 2 +- unittest/constants.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'unittest') diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h index f905794..7de2177 100644 --- a/include/stick10_commands_0.8.h +++ b/include/stick10_commands_0.8.h @@ -49,7 +49,7 @@ namespace nitrokey { public: struct CommandPayload { uint8_t temporary_admin_password[25]; - uint8_t type; //0-secret, 1-name + uint8_t type; //S-secret, N-name uint8_t id; //multiple reports for values longer than 30 bytes uint8_t data[30]; //data, does not need null termination diff --git a/unittest/constants.py b/unittest/constants.py index 3c19a9b..0897b42 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -2,7 +2,7 @@ from enum import Enum from misc import to_hex RFC_SECRET_HR = '12345678901234567890' -RFC_SECRET = to_hex(RFC_SECRET_HR) # '3031323334353637383930...' +RFC_SECRET = to_hex(RFC_SECRET_HR) # '31323334353637383930...' # print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) ) -- cgit v1.2.3 From b9f7d118ecdef61764c3256a203010831e0c5d7d Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 26 Nov 2016 15:41:34 +0100 Subject: Test for manual checking of TOTP slot written by Nitrokey App Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index c8be0e8..89a6ccf 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -683,3 +683,23 @@ def test_edit_OTP_slot(C): assert gs(C.NK_get_hotp_slot_name(slot_number)) == changed_name +@pytest.mark.skip +@pytest.mark.parametrize("secret", ['31323334353637383930'*2,'31323334353637383930'*4] ) +def test_TOTP_codes_from_nitrokeyapp(secret, C): + """ + Helper test for manual TOTP check of written secret by Nitrokey App + Destined to run by hand + """ + slot_number = 0 + PIN_protection = False + period = 30 + assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection, + DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + code_device = str(C.NK_get_totp_code(slot_number, 0, 0, period)) + code_device = '0'+code_device if len(code_device) < 6 else code_device + + oath = pytest.importorskip("oath") + lib_at = lambda : oath.totp(secret, period=period) + print (lib_at()) + assert lib_at() == code_device -- cgit v1.2.3 From f4b1f29058f55a716cb6e4e8a4f9bf0e6c7332fe Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Sat, 26 Nov 2016 15:42:48 +0100 Subject: Add missing newlines to make code format consistent Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 89a6ccf..5b39c34 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -4,6 +4,7 @@ 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_pro_rtm_08, is_storage + def test_enable_password_safe(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe('wrong_password') == DeviceErrorCode.WRONG_PASSWORD @@ -252,6 +253,7 @@ def test_HOTP_token(C): assert hotp_code != 0 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK + def test_HOTP_counters(C): """ # https://tools.ietf.org/html/rfc4226#page-32 @@ -374,6 +376,7 @@ def test_TOTP_RFC_usepin(C, PIN_protection): correct += expected_code == code_from_device assert data == responses or correct == len(test_data) + def test_get_slot_names(C): C.NK_set_debug(True) assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK @@ -502,6 +505,7 @@ def test_get_serial_number(C): assert len(sn) > 0 print(('Serial number of the device: ', sn)) + @pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ]) def test_OTP_secret_started_from_null(C, secret): ''' @@ -577,6 +581,7 @@ def test_TOTP_slots_read_write_at_time_period(C, time, period): lib_res += (time, lib_at(time)) assert dev_res == lib_res + @pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] ) def test_TOTP_secrets(C, secret): ''' @@ -606,6 +611,7 @@ def test_TOTP_secrets(C, secret): lib_res += (time, lib_at(time)) assert dev_res == lib_res + @pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] ) def test_HOTP_secrets(C, secret): ''' @@ -653,6 +659,7 @@ def test_special_double_press(C): DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK # requires manual check + def test_edit_OTP_slot(C): """ should change slots counter and name without changing its secret (using null secret for second update) -- cgit v1.2.3 From 279a310d6710908943237f5528d64a94ecd45885 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 6 Dec 2016 20:00:42 +0100 Subject: Tests: check are long OTP secrets supported Signed-off-by: Szczepan Zalega --- unittest/misc.py | 24 ++++++++++++++---------- unittest/test_library.py | 4 +++- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'unittest') diff --git a/unittest/misc.py b/unittest/misc.py index 8296814..f4d7731 100644 --- a/unittest/misc.py +++ b/unittest/misc.py @@ -20,25 +20,29 @@ def cast_pointer_to_tuple(obj, typen, len): # 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] + +def get_devices_firmware_version(C): + firmware = C.NK_get_major_firmware_version() return firmware def is_pro_rtm_07(C): - firmware = get_firmware_version_from_status(C) - return '07 00' in firmware + firmware = get_devices_firmware_version(C) + return firmware == 7 + def is_pro_rtm_08(C): - firmware = get_firmware_version_from_status(C) - return '08 00' in firmware + firmware = get_devices_firmware_version(C) + return firmware == 8 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 + # TODO identify connected device directly + return not is_pro_rtm_08(C) and not is_pro_rtm_07(C) + + +def is_long_OTP_secret_handled(C): + return is_pro_rtm_08(C) or is_storage(C) and get_devices_firmware_version(C) > 43 diff --git a/unittest/test_library.py b/unittest/test_library.py index d0eef80..7b05c58 100644 --- a/unittest/test_library.py +++ b/unittest/test_library.py @@ -1,6 +1,6 @@ import pytest -from misc import ffi, gs, to_hex +from misc import ffi, gs, to_hex, is_pro_rtm_07, is_long_OTP_secret_handled from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors def test_too_long_strings(C): @@ -50,6 +50,8 @@ def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string): def test_warning_binary_bigger_than_secret_buffer(C): invalid_hex_string = to_hex('1234567890') * 3 + if is_long_OTP_secret_handled(C): + invalid_hex_string *= 2 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 -- cgit v1.2.3 From 4ab91aae6c101b72a94d3785dbdad117354b87d5 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 6 Dec 2016 20:22:37 +0100 Subject: Check maximum OTP secret size in new authorization style commands Authenticate before testing invalid hex strings Remove invalid test for empty string for writing otp slot (empty string allows editing) Signed-off-by: Szczepan Zalega --- NitrokeyManager.cc | 4 ++++ unittest/test_library.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index da31c8d..a15b9c8 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -302,6 +302,10 @@ namespace nitrokey{ payload2.id = 0; auto secret_bin = misc::hex_string_to_byte(secret); auto remaining_secret_length = secret_bin.size(); + const auto maximum_OTP_secret_size = 40; + if(remaining_secret_length > maximum_OTP_secret_size){ + throw TargetBufferSmallerThanSource(remaining_secret_length, maximum_OTP_secret_size); + } while (remaining_secret_length>0){ const auto bytesToCopy = std::min(sizeof(payload2.data), remaining_secret_length); diff --git a/unittest/test_library.py b/unittest/test_library.py index 7b05c58..bd44e89 100644 --- a/unittest/test_library.py +++ b/unittest/test_library.py @@ -36,12 +36,13 @@ def test_invalid_slot(C): 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]) + ['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_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 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, "", -- cgit v1.2.3 From 60d744fec7d20fb93b5152f3a7db0101009831cb Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 11:09:07 +0100 Subject: Allow to skip test if device is not able to pass it Signed-off-by: Szczepan Zalega --- unittest/conftest.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/unittest/conftest.py b/unittest/conftest.py index 68227d5..88bf7d0 100644 --- a/unittest/conftest.py +++ b/unittest/conftest.py @@ -2,6 +2,16 @@ import pytest from misc import ffi +device_type = None + +def skip_if_device_version_lower_than(allowed_devices): + global device_type + model, version = device_type + infinite_version_number = 999 + if allowed_devices.get(model, infinite_version_number) > version: + pytest.skip('This device model is not applicable to run this test') + + @pytest.fixture(scope="module") def C(request): fp = '../NK_C_API.h' @@ -24,7 +34,11 @@ def C(request): 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 nk_login != 0 # returns 0 if not connected or wrong model or 1 when connected + global device_type + firmware_version = C.NK_get_major_firmware_version() + model = 'P' if firmware_version in [7,8] else 'S' + device_type = (model, firmware_version) # 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 -- cgit v1.2.3 From e75f9a54e0a696de47f00dce980b1a3b9feddee2 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 11:09:45 +0100 Subject: Code reformat in library test Signed-off-by: Szczepan Zalega --- unittest/test_library.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/unittest/test_library.py b/unittest/test_library.py index bd44e89..ec00a5a 100644 --- a/unittest/test_library.py +++ b/unittest/test_library.py @@ -3,6 +3,7 @@ import pytest from misc import ffi, gs, to_hex, is_pro_rtm_07, is_long_OTP_secret_handled from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors + def test_too_long_strings(C): new_password = '123123123' long_string = 'a' * 100 @@ -35,6 +36,7 @@ def test_invalid_slot(C): 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): @@ -48,7 +50,6 @@ def test_invalid_secret_hex_string_for_OTP_write(C, 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 if is_long_OTP_secret_handled(C): -- cgit v1.2.3 From 8e1499871dda0559b0d7164e23d9e146f10409ec Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 11:10:35 +0100 Subject: Apply firmware versions limits to tests Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 5b39c34..7f567c7 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -1,5 +1,6 @@ import pytest +from conftest import skip_if_device_version_lower_than 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_pro_rtm_08, is_storage @@ -511,6 +512,8 @@ def test_OTP_secret_started_from_null(C, secret): ''' NK Pro 0.8+, NK Storage 0.43+ ''' + skip_if_device_version_lower_than({'S': 43, 'P': 8}) + oath = pytest.importorskip("oath") lib_at = lambda t: oath.hotp(secret, t, format='dec6') PIN_protection = False @@ -532,8 +535,18 @@ def test_OTP_secret_started_from_null(C, secret): assert dev_res == lib_res -@pytest.mark.parametrize("counter", [0, 3, 7, 0xffff, 0xffffffff, 0xffffffffffffffff] ) +@pytest.mark.parametrize("counter", [0, 3, 7, 0xffff, + 0xffffffff, + 0xffffffffffffffff] ) def test_HOTP_slots_read_write_counter(C, counter): + """ + Write different counters to all HOTP slots, read code and compare with 3rd party + :param counter: + """ + if counter >= 1e7: + # Storage does not handle counters longer than 7 digits + skip_if_device_version_lower_than({'P': 7}) + secret = RFC_SECRET oath = pytest.importorskip("oath") lib_at = lambda t: oath.hotp(secret, t, format='dec6') @@ -587,6 +600,8 @@ def test_TOTP_secrets(C, secret): ''' NK Pro 0.8+, NK Storage 0.44+ ''' + skip_if_device_version_lower_than({'S': 44, 'P': 8}) + if is_pro_rtm_07(C) and len(secret)>20*2: #*2 since secret is in hex pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 ") slot_number = 0 @@ -617,6 +632,8 @@ def test_HOTP_secrets(C, secret): ''' NK Pro 0.8+, NK Storage 0.44+ ''' + skip_if_device_version_lower_than({'S': 44, 'P': 8}) + if is_pro_rtm_07(C) and len(secret)>20*2: #*2 since secret is in hex pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 ") slot_number = 0 -- cgit v1.2.3 From 7c432494269144fa9777266834fd5b88b4fe1b90 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 11:12:24 +0100 Subject: Add NK Storage specific reply on running not initialized Password Safe Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 7f567c7..6b47c9f 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -81,7 +81,9 @@ def test_enable_password_safe_after_factory_reset(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK wait(10) - assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_AES_DEC_FAILED + enable_password_safe_result = C.NK_enable_password_safe(DefaultPasswords.USER) + assert enable_password_safe_result == DeviceErrorCode.STATUS_AES_DEC_FAILED \ + or is_storage(C) and enable_password_safe_result == DeviceErrorCode.WRONG_PASSWORD assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK -- cgit v1.2.3 From b78c28cb133bd00024416d8ad69740040c91d589 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 11:13:20 +0100 Subject: Enable factory reset test for Nitrokey Storage. Comments. Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 6b47c9f..3282436 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -476,8 +476,6 @@ def test_read_write_config(C): def test_factory_reset(C): - if is_storage(C): - pytest.skip('Recovery not implemented for NK Storage') C.NK_set_debug(True) assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK @@ -494,6 +492,8 @@ def test_factory_reset(C): assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK + if is_storage(C): + C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) def test_get_status(C): @@ -573,6 +573,10 @@ def test_HOTP_slots_read_write_counter(C, counter): @pytest.mark.parametrize("period", [30,60] ) @pytest.mark.parametrize("time", range(21,70,20) ) def test_TOTP_slots_read_write_at_time_period(C, time, period): + """ + Write to all TOTP slots with specified period, read code at specified time + and compare with 3rd party + """ secret = RFC_SECRET oath = pytest.importorskip("oath") lib_at = lambda t: oath.totp(RFC_SECRET, t=t, period=period) -- cgit v1.2.3 From e513f430851ad9645aff53df32813a5343c697c1 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 12:43:24 +0100 Subject: Skip non-Storage and old devices for Storage tests Signed-off-by: Szczepan Zalega --- unittest/test_storage.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'unittest') diff --git a/unittest/test_storage.py b/unittest/test_storage.py index 01276ce..a1c59aa 100644 --- a/unittest/test_storage.py +++ b/unittest/test_storage.py @@ -1,9 +1,10 @@ -import pytest +import pprint -from misc import ffi, gs, wait, cast_pointer_to_tuple -from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors +import pytest -import pprint +from conftest import skip_if_device_version_lower_than +from constants import DefaultPasswords, DeviceErrorCode +from misc import gs, wait pprint = pprint.PrettyPrinter(indent=4).pprint @@ -22,6 +23,7 @@ def get_dict_from_dissect(status): def test_get_status_storage(C): + skip_if_device_version_lower_than({'S': 43}) status_pointer = C.NK_get_status_storage_as_string() assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK status_string = gs(status_pointer) @@ -32,6 +34,7 @@ def test_get_status_storage(C): def test_sd_card_usage(C): + skip_if_device_version_lower_than({'S': 43}) 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) @@ -41,11 +44,13 @@ def test_sd_card_usage(C): def test_encrypted_volume_unlock(C): + skip_if_device_version_lower_than({'S': 43}) 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): + skip_if_device_version_lower_than({'S': 43}) hidden_volume_password = 'hiddenpassword' assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK @@ -54,6 +59,7 @@ def test_encrypted_volume_unlock_hidden(C): @pytest.mark.skip(reason='hangs device, to report') def test_encrypted_volume_setup_multiple_hidden(C): + skip_if_device_version_lower_than({'S': 43}) hidden_volume_password = 'hiddenpassword' p = lambda i: hidden_volume_password + str(i) assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK @@ -67,25 +73,30 @@ def test_encrypted_volume_setup_multiple_hidden(C): def test_unencrypted_volume_set_read_only(C): + skip_if_device_version_lower_than({'S': 43}) 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): + skip_if_device_version_lower_than({'S': 43}) 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): + skip_if_device_version_lower_than({'S': 43}) assert C.NK_export_firmware(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK def test_clear_new_sd_card_notification(C): + skip_if_device_version_lower_than({'S': 43}) assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK @pytest.mark.skip def test_fill_SD_card(C): + skip_if_device_version_lower_than({'S': 43}) status = C.NK_fill_SD_card_with_random_data(DefaultPasswords.ADMIN) assert status == DeviceErrorCode.STATUS_OK or status == DeviceErrorCode.BUSY while 1: @@ -97,12 +108,14 @@ def test_fill_SD_card(C): def test_get_busy_progress_on_idle(C): + skip_if_device_version_lower_than({'S': 43}) 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): + skip_if_device_version_lower_than({'S': 43}) 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 @@ -110,5 +123,6 @@ def test_change_update_password(C): def test_send_startup(C): + skip_if_device_version_lower_than({'S': 43}) time_seconds_from_epoch = 0 # FIXME set proper date assert C.NK_send_startup(time_seconds_from_epoch) == DeviceErrorCode.STATUS_OK -- cgit v1.2.3 From 87eaf78c7c7290764ccaebe67726b77a44f21240 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 12:44:33 +0100 Subject: Remove old skipping code. Feature comment. Signed-off-by: Szczepan Zalega --- unittest/test_library.py | 5 ----- unittest/test_pro.py | 7 +++---- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'unittest') diff --git a/unittest/test_library.py b/unittest/test_library.py index ec00a5a..b24c72a 100644 --- a/unittest/test_library.py +++ b/unittest/test_library.py @@ -58,11 +58,6 @@ def test_warning_binary_bigger_than_secret_buffer(C): 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' diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 3282436..ab30e04 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -635,13 +635,12 @@ def test_TOTP_secrets(C, secret): @pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] ) def test_HOTP_secrets(C, secret): - ''' + """ NK Pro 0.8+, NK Storage 0.44+ - ''' + feature needed: support for 320bit secrets + """ skip_if_device_version_lower_than({'S': 44, 'P': 8}) - if is_pro_rtm_07(C) and len(secret)>20*2: #*2 since secret is in hex - pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 ") slot_number = 0 counter = 0 oath = pytest.importorskip("oath") -- cgit v1.2.3 From 1d493a5daba996e31615154b28688de3637529c7 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 13:46:54 +0100 Subject: Test null started OTP secrets also for 320bit case Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index ab30e04..d2ed48b 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -509,12 +509,16 @@ def test_get_serial_number(C): print(('Serial number of the device: ', sn)) -@pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ]) +@pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102', + '00'*29+'ff', '00'*39+'ff', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ]) def test_OTP_secret_started_from_null(C, secret): - ''' + """ NK Pro 0.8+, NK Storage 0.43+ - ''' + """ skip_if_device_version_lower_than({'S': 43, 'P': 8}) + if len(secret) > 40: + # feature: 320 bit long secret handling + skip_if_device_version_lower_than({'S': 44, 'P': 8}) oath = pytest.importorskip("oath") lib_at = lambda t: oath.hotp(secret, t, format='dec6') -- cgit v1.2.3 From e414840ab19ce022d5354fad13bcbcbe41925eea Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 9 Dec 2016 13:49:33 +0100 Subject: Add a note regarding Password Safe tests Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index d2ed48b..2a4a1a7 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -7,6 +7,9 @@ from misc import is_pro_rtm_07, is_pro_rtm_08, is_storage def test_enable_password_safe(C): + """ + All Password Safe tests depend on AES keys being initialized. They will fail otherwise. + """ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe('wrong_password') == DeviceErrorCode.WRONG_PASSWORD assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK -- cgit v1.2.3 From 925d75007161aa845e8c29ac412ac875cf45f337 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Mon, 12 Dec 2016 15:10:09 +0100 Subject: Tests: clear new SD card warning on factory reset Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 2a4a1a7..a6f7094 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -84,6 +84,8 @@ def test_enable_password_safe_after_factory_reset(C): assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK wait(10) + if is_storage(C): + assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK enable_password_safe_result = C.NK_enable_password_safe(DefaultPasswords.USER) assert enable_password_safe_result == DeviceErrorCode.STATUS_AES_DEC_FAILED \ or is_storage(C) and enable_password_safe_result == DeviceErrorCode.WRONG_PASSWORD @@ -496,7 +498,7 @@ def test_factory_reset(C): assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK if is_storage(C): - C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) + assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK def test_get_status(C): -- cgit v1.2.3 From 5e02e1e198c1c4051f15c45dfeb67b9be2cd5aa1 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Mon, 12 Dec 2016 15:10:55 +0100 Subject: Tests: skip edit OTP slot test for NK Storage 0.44 Signed-off-by: Szczepan Zalega --- unittest/test_pro.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'unittest') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index a6f7094..4a2a504 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -695,6 +695,9 @@ def test_edit_OTP_slot(C): """ should change slots counter and name without changing its secret (using null secret for second update) """ + # counter does not reset under Storage v0.43 + skip_if_device_version_lower_than({'S': 44, 'P': 7}) + secret = RFC_SECRET counter = 0 PIN_protection = False -- cgit v1.2.3