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(-) 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.1