summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzczepan Zalega <szczepan@nitrokey.com>2016-11-16 18:32:38 +0100
committerSzczepan Zalega <szczepan@nitrokey.com>2016-12-03 16:01:50 +0100
commitcbccc871329c5522449010ae5007278123508820 (patch)
tree0362686697508b3f3438ba437fdd5c83389abaad
parent54d3b649b4c6d51120c16c64b7f824c81123a807 (diff)
downloadlibnitrokey-cbccc871329c5522449010ae5007278123508820.tar.gz
libnitrokey-cbccc871329c5522449010ae5007278123508820.tar.bz2
Use another OTP writing protocol and test it
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
-rw-r--r--NitrokeyManager.cc67
-rw-r--r--command_id.cc3
-rw-r--r--include/command_id.h1
-rw-r--r--include/stick10_commands_0.8.h111
-rw-r--r--unittest/constants.py2
-rw-r--r--unittest/test3.cc111
-rw-r--r--unittest/test_pro.py33
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<stick10_08::WriteToHOTPSlot>();
- strcpyT(payload.temporary_admin_password, temporary_password);
+ auto payload2 = get_payload<stick10_08::SendOTPData>();
+ 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<stick10_08::SendOTPData>();
+ 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<stick10_08::WriteToOTPSlot>();
+ 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<stick10_08::WriteToHOTPSlot_2>();
- 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<stick10_08::WriteToTOTPSlot>();
- strcpyT(payload.temporary_admin_password, temporary_password);
+
+ auto payload2 = get_payload<stick10_08::SendOTPData>();
+ 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<stick10_08::SendOTPData>();
+ 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<stick10_08::WriteToOTPSlot>();
+ 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<stick10_08::WriteToTOTPSlot_2>();
- 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 , int, EnumClassHash>({
- {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<CommandID::WRITE_TO_SLOT> {
+ class SendOTPData : Command<CommandID::SEND_OTP_DATA> {
//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<CommandID::WRITE_TO_SLOT_2> {
+ class WriteToOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
+ //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<command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
- };
-
-
- class WriteToTOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
- //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<CommandID::WRITE_TO_SLOT_2> {
- 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<command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
- };
-
-
class GetHOTP : Command<CommandID::GET_CODE> {
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<WriteToHOTPSlot>();
- 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<SendOTPData>();
+ 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<WriteToHOTPSlot_2>();
+ p2 = get_payload<SendOTPData>();
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<WriteToOTPSlot>();
+ 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<WriteGeneralConfig>();
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<WriteGeneralConfig>();
- p.enable_user_password = 1;
- strcpyT(p.temporary_admin_password, temporary_password);
- WriteGeneralConfig::CommandTransaction::run(stick, p);
+ {
+ auto p = get_payload<WriteGeneralConfig>();
+ p.enable_user_password = 1;
+ strcpyT(p.temporary_admin_password, temporary_password);
+ WriteGeneralConfig::CommandTransaction::run(stick, p);
+ }
+
+ auto p2 = get_payload<SendOTPData>();
+ 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<SendOTPData>();
+ 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<WriteToHOTPSlot>();
- 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<WriteToOTPSlot>();
+ 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<WriteToHOTPSlot_2>();
- 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<GetHOTP>();
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<WriteGeneralConfig>();
- p.enable_user_password = 1;
- strcpyT(p.temporary_admin_password, temporary_password);
- WriteGeneralConfig::CommandTransaction::run(stick, p);
+ {
+ auto p = get_payload<WriteGeneralConfig>();
+ p.enable_user_password = 1;
+ strcpyT(p.temporary_admin_password, temporary_password);
+ WriteGeneralConfig::CommandTransaction::run(stick, p);
+ }
- auto pw = get_payload<WriteToTOTPSlot>();
- 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<WriteToTOTPSlot_2>();
- 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<SendOTPData>();
+ 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<SendOTPData>();
+ 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<WriteToOTPSlot>();
+ 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<GetTOTP>();
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
+
+