From 9e1f590d5a929a21223bb39ae5123d27e69fda66 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Fri, 17 Jan 2020 18:19:45 +0100 Subject: Add tests for Pro`s bootloader --- unittest/test_pro.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'unittest/test_pro.py') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index a8df7cd..3f1cb8d 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -977,21 +977,48 @@ def test_get_device_model(C): # assert C.NK_get_device_model() != C.NK_DISCONNECTED +@pytest.mark.firmware +def test_bootloader_password_change_pro_length(C): + skip_if_device_version_lower_than({'P': 11}) + + # Test whether the correct password is set + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + # Change to the longest possible password + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_LONG) == DeviceErrorCode.STATUS_OK + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_LONG, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + # Use longer or shorter passwords than possible + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_LONG) == LibraryErrors.TOO_LONG_STRING + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_SHORT) == DeviceErrorCode.WRONG_PASSWORD + + + @pytest.mark.firmware def test_bootloader_password_change_pro(C): skip_if_device_version_lower_than({'P': 11}) assert C.NK_change_firmware_password_pro(b'zxcasd', b'zxcasd') == DeviceErrorCode.WRONG_PASSWORD + # Revert effects of broken test run, if needed + C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) + + # Change to the same password + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + # Change password assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK @pytest.mark.firmware -def test_bootloader_run_pro(C): +def test_bootloader_run_pro_wrong_password(C): skip_if_device_version_lower_than({'P': 11}) assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD + + +@pytest.mark.skip +@pytest.mark.firmware +def test_bootloader_run_pro(C): # Not enabled due to lack of side-effect removal at this point - # assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK @pytest.mark.firmware -- cgit v1.2.3 From 4d6a01cd2f2ec6440f5dd2c02048cbb413f91f5d Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Mon, 27 Jan 2020 18:18:55 +0100 Subject: Correct result of successful execution for bootloader to DISCONNECTED --- unittest/constants.py | 1 + unittest/test_pro.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'unittest/test_pro.py') diff --git a/unittest/constants.py b/unittest/constants.py index bf4f5b9..9e04701 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -52,6 +52,7 @@ class DeviceErrorCode: STATUS_NOT_AUTHORIZED = 5 STATUS_AES_DEC_FAILED = 0xa STATUS_UNKNOWN_ERROR = 100 + STATUS_DISCONNECTED = 255 class LibraryErrors: diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 3f1cb8d..d26aa7c 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -1018,7 +1018,7 @@ def test_bootloader_run_pro_wrong_password(C): @pytest.mark.firmware def test_bootloader_run_pro(C): # Not enabled due to lack of side-effect removal at this point - assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED @pytest.mark.firmware -- cgit v1.2.3 From 73251ccf5abae5ae91f2aa962a29dce6fcc0e5e4 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Mon, 27 Jan 2020 19:14:09 +0100 Subject: Rename the actual bootloader switching test to a distinct name --- unittest/test_pro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'unittest/test_pro.py') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index d26aa7c..3a324bb 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -1016,7 +1016,7 @@ def test_bootloader_run_pro_wrong_password(C): @pytest.mark.skip @pytest.mark.firmware -def test_bootloader_run_pro(C): +def test_bootloader_run_pro_real(C): # Not enabled due to lack of side-effect removal at this point assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED -- cgit v1.2.3 From ec767410d860688f32ef644e9573841272d5aec2 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 28 Jan 2020 14:45:34 +0100 Subject: Skip bootloader activation unless specified by the switch --- unittest/conftest.py | 8 ++++++++ unittest/test_pro.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'unittest/test_pro.py') diff --git a/unittest/conftest.py b/unittest/conftest.py index 49b4f02..1377e50 100644 --- a/unittest/conftest.py +++ b/unittest/conftest.py @@ -155,3 +155,11 @@ def get_library(request, allow_offline=False): return AttrProxy(C, "libnitrokey C") + +def pytest_addoption(parser): + parser.addoption("--run-skipped", action="store_true", + help="run the tests skipped by default, e.g. adding side effects") + +def pytest_runtest_setup(item): + if 'skip_by_default' in item.keywords and not item.config.getoption("--run-skipped"): + pytest.skip("need --run-skipped option to run this test") \ No newline at end of file diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 3a324bb..a47d804 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -1014,7 +1014,7 @@ def test_bootloader_run_pro_wrong_password(C): assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD -@pytest.mark.skip +@pytest.mark.skip_by_default @pytest.mark.firmware def test_bootloader_run_pro_real(C): # Not enabled due to lack of side-effect removal at this point -- cgit v1.2.3 From 2b37ae486deba1985179543333c592b55fafbc37 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 28 Jan 2020 14:46:04 +0100 Subject: Bootloader data retention test --- unittest/test_pro.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'unittest/test_pro.py') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index a47d804..4ac19ab 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -1029,6 +1029,23 @@ def test_bootloader_password_change_pro_too_long(C): assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING +@pytest.mark.skip_by_default +@pytest.mark.firmware +def test_bootloader_data_rention_test(C): + skip_if_device_version_lower_than({'P': 11}) + + def populate_device(): + return False + + def check_data_on_device(): + return False + + assert populate_device() + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED + input('Please press ENTER after uploading new firmware to the device') + assert check_data_on_device() + + @pytest.mark.otp @pytest.mark.parametrize('counter_mid', [10**3-1, 10**4-1, 10**7-1, 10**8-10, 2**16, 2**31-1, 2**32-1, 2**33, 2**50, 2**60, 2**63]) # 2**64-1 def test_HOTP_counter_getter(C, counter_mid: int): -- cgit v1.2.3 From 0f0225c63b6770c62ca13fcd31fd0e3e83674d93 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 28 Jan 2020 14:49:26 +0100 Subject: Refactor rename --- unittest/test_pro.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'unittest/test_pro.py') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 4ac19ab..2a5f02a 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -88,21 +88,21 @@ def test_write_all_password_safe_slots_and_read_10_times(C): @pytest.mark.slowtest @pytest.mark.xfail(reason="This test should be run directly after test_write_all_password_safe_slots_and_read_10_times") def test_read_all_password_safe_slots_10_times(C): - def fill(s, wid): - assert wid >= len(s) + def helper_fill(str_to_fill, target_width): + assert target_width >= len(str_to_fill) numbers = '1234567890'*4 - s += numbers[:wid-len(s)] - assert len(s) == wid - return bb(s) + str_to_fill += numbers[:target_width - len(str_to_fill)] + assert len(str_to_fill) == target_width + return bb(str_to_fill) - def get_pass(suffix): - return fill('pass' + suffix, 20) + def helper_PWS_get_pass(suffix): + return helper_fill('pass' + suffix, 20) - def get_loginname(suffix): - return fill('login' + suffix, 32) + def helper_PWS_get_loginname(suffix): + return helper_fill('login' + suffix, 32) - def get_slotname(suffix): - return fill('slotname' + suffix, 11) + def helper_PWS_get_slotname(suffix): + return helper_fill('slotname' + suffix, 11) assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK @@ -111,9 +111,9 @@ def test_read_all_password_safe_slots_10_times(C): for j in range(0, 10): for i in range(0, PWS_slot_count): iss = str(i) - assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss) - assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss) - assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss) + assert gs(C.NK_get_password_safe_slot_name(i)) == helper_PWS_get_slotname(iss) + assert gs(C.NK_get_password_safe_slot_login(i)) == helper_PWS_get_loginname(iss) + assert gs(C.NK_get_password_safe_slot_password(i)) == helper_PWS_get_pass(iss) @pytest.mark.lock_device -- cgit v1.2.3 From 59ca679eeb272163e4ebbb2f01231adc4820cda1 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 28 Jan 2020 14:52:09 +0100 Subject: Refactor move --- unittest/constants.py | 2 -- unittest/helpers.py | 22 ++++++++++++++++++++++ unittest/test_pro.py | 19 ++----------------- 3 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 unittest/helpers.py (limited to 'unittest/test_pro.py') diff --git a/unittest/constants.py b/unittest/constants.py index 1ba52fa..b73dfe8 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -21,8 +21,6 @@ SPDX-License-Identifier: LGPL-3.0 from misc import to_hex -def bb(x): - return bytes(x, encoding='ascii') RFC_SECRET_HR = '12345678901234567890' diff --git a/unittest/helpers.py b/unittest/helpers.py new file mode 100644 index 0000000..79f4e1e --- /dev/null +++ b/unittest/helpers.py @@ -0,0 +1,22 @@ +def bb(x): + return bytes(x, encoding='ascii') + + +def helper_fill(str_to_fill, target_width): + assert target_width >= len(str_to_fill) + numbers = '1234567890' * 4 + str_to_fill += numbers[:target_width - len(str_to_fill)] + assert len(str_to_fill) == target_width + return bb(str_to_fill) + + +def helper_PWS_get_pass(suffix): + return helper_fill('pass' + suffix, 20) + + +def helper_PWS_get_loginname(suffix): + return helper_fill('login' + suffix, 32) + + +def helper_PWS_get_slotname(suffix): + return helper_fill('slotname' + suffix, 11) \ No newline at end of file diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 2a5f02a..233d4d2 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -22,8 +22,9 @@ SPDX-License-Identifier: LGPL-3.0 import pytest from conftest import skip_if_device_version_lower_than -from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bb, bbRFC_SECRET, LibraryErrors, HOTP_slot_count, \ +from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bbRFC_SECRET, LibraryErrors, HOTP_slot_count, \ TOTP_slot_count +from helpers import helper_PWS_get_slotname, helper_PWS_get_loginname, helper_PWS_get_pass, bb from misc import ffi, gs, wait, cast_pointer_to_tuple, has_binary_counter from misc import is_storage @@ -88,22 +89,6 @@ def test_write_all_password_safe_slots_and_read_10_times(C): @pytest.mark.slowtest @pytest.mark.xfail(reason="This test should be run directly after test_write_all_password_safe_slots_and_read_10_times") def test_read_all_password_safe_slots_10_times(C): - def helper_fill(str_to_fill, target_width): - assert target_width >= len(str_to_fill) - numbers = '1234567890'*4 - str_to_fill += numbers[:target_width - len(str_to_fill)] - assert len(str_to_fill) == target_width - return bb(str_to_fill) - - def helper_PWS_get_pass(suffix): - return helper_fill('pass' + suffix, 20) - - def helper_PWS_get_loginname(suffix): - return helper_fill('login' + suffix, 32) - - def helper_PWS_get_slotname(suffix): - return helper_fill('slotname' + suffix, 11) - assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK PWS_slot_count = 16 -- cgit v1.2.3 From 2fa7e29b725085e62dd647da6e0dd28bcfe91e43 Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 28 Jan 2020 14:53:41 +0100 Subject: Refactor move 2 --- unittest/test_pro.py | 69 -------------------------------------- unittest/test_pro_bootloader.py | 73 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 69 deletions(-) create mode 100644 unittest/test_pro_bootloader.py (limited to 'unittest/test_pro.py') diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 233d4d2..13efd28 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -962,75 +962,6 @@ def test_get_device_model(C): # assert C.NK_get_device_model() != C.NK_DISCONNECTED -@pytest.mark.firmware -def test_bootloader_password_change_pro_length(C): - skip_if_device_version_lower_than({'P': 11}) - - # Test whether the correct password is set - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK - # Change to the longest possible password - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_LONG) == DeviceErrorCode.STATUS_OK - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_LONG, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK - # Use longer or shorter passwords than possible - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_LONG) == LibraryErrors.TOO_LONG_STRING - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_SHORT) == DeviceErrorCode.WRONG_PASSWORD - - - -@pytest.mark.firmware -def test_bootloader_password_change_pro(C): - skip_if_device_version_lower_than({'P': 11}) - assert C.NK_change_firmware_password_pro(b'zxcasd', b'zxcasd') == DeviceErrorCode.WRONG_PASSWORD - - # Revert effects of broken test run, if needed - C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) - - # Change to the same password - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK - # Change password - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK - - -@pytest.mark.firmware -def test_bootloader_run_pro_wrong_password(C): - skip_if_device_version_lower_than({'P': 11}) - assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD - - -@pytest.mark.skip_by_default -@pytest.mark.firmware -def test_bootloader_run_pro_real(C): - # Not enabled due to lack of side-effect removal at this point - assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED - - -@pytest.mark.firmware -def test_bootloader_password_change_pro_too_long(C): - skip_if_device_version_lower_than({'P': 11}) - long_string = b'a' * 100 - assert C.NK_change_firmware_password_pro(long_string, long_string) == LibraryErrors.TOO_LONG_STRING - assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING - - -@pytest.mark.skip_by_default -@pytest.mark.firmware -def test_bootloader_data_rention_test(C): - skip_if_device_version_lower_than({'P': 11}) - - def populate_device(): - return False - - def check_data_on_device(): - return False - - assert populate_device() - assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED - input('Please press ENTER after uploading new firmware to the device') - assert check_data_on_device() - - @pytest.mark.otp @pytest.mark.parametrize('counter_mid', [10**3-1, 10**4-1, 10**7-1, 10**8-10, 2**16, 2**31-1, 2**32-1, 2**33, 2**50, 2**60, 2**63]) # 2**64-1 def test_HOTP_counter_getter(C, counter_mid: int): diff --git a/unittest/test_pro_bootloader.py b/unittest/test_pro_bootloader.py new file mode 100644 index 0000000..4a6b272 --- /dev/null +++ b/unittest/test_pro_bootloader.py @@ -0,0 +1,73 @@ +import pytest + +from conftest import skip_if_device_version_lower_than +from constants import DefaultPasswords, DeviceErrorCode, LibraryErrors + + +@pytest.mark.firmware +def test_bootloader_password_change_pro_length(C): + skip_if_device_version_lower_than({'P': 11}) + + # Test whether the correct password is set + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + # Change to the longest possible password + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_LONG) == DeviceErrorCode.STATUS_OK + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_LONG, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + # Use longer or shorter passwords than possible + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_LONG) == LibraryErrors.TOO_LONG_STRING + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_SHORT) == DeviceErrorCode.WRONG_PASSWORD + + + +@pytest.mark.firmware +def test_bootloader_password_change_pro(C): + skip_if_device_version_lower_than({'P': 11}) + assert C.NK_change_firmware_password_pro(b'zxcasd', b'zxcasd') == DeviceErrorCode.WRONG_PASSWORD + + # Revert effects of broken test run, if needed + C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) + + # Change to the same password + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + # Change password + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK + + +@pytest.mark.firmware +def test_bootloader_run_pro_wrong_password(C): + skip_if_device_version_lower_than({'P': 11}) + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD + + +@pytest.mark.skip_by_default +@pytest.mark.firmware +def test_bootloader_run_pro_real(C): + # Not enabled due to lack of side-effect removal at this point + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED + + +@pytest.mark.firmware +def test_bootloader_password_change_pro_too_long(C): + skip_if_device_version_lower_than({'P': 11}) + long_string = b'a' * 100 + assert C.NK_change_firmware_password_pro(long_string, long_string) == LibraryErrors.TOO_LONG_STRING + assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING + + +@pytest.mark.skip_by_default +@pytest.mark.firmware +def test_bootloader_data_rention_test(C): + skip_if_device_version_lower_than({'P': 11}) + + def populate_device(): + return False + + def check_data_on_device(): + return False + + assert populate_device() + assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED + input('Please press ENTER after uploading new firmware to the device') + assert check_data_on_device() -- cgit v1.2.3 From 55f9b7293c22bbedd5a972fa8f1946dfd57d9c7a Mon Sep 17 00:00:00 2001 From: Szczepan Zalega Date: Tue, 25 Feb 2020 14:10:30 +0100 Subject: Data retention test. Refactoring. Helper functions. --- unittest/constants.py | 8 +++----- unittest/helpers.py | 35 ++++++++++++++++++++++++++++++++--- unittest/misc.py | 4 ++++ unittest/test_multiple.py | 4 ++-- unittest/test_pro.py | 30 +++++++----------------------- unittest/test_pro_bootloader.py | 17 +++++++---------- unittest/test_storage.py | 4 ++-- 7 files changed, 57 insertions(+), 45 deletions(-) (limited to 'unittest/test_pro.py') diff --git a/unittest/constants.py b/unittest/constants.py index b73dfe8..4047f59 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -18,10 +18,7 @@ along with libnitrokey. If not, see . SPDX-License-Identifier: LGPL-3.0 """ - -from misc import to_hex - - +from misc import to_hex, bb RFC_SECRET_HR = '12345678901234567890' RFC_SECRET = to_hex(RFC_SECRET_HR) # '31323334353637383930...' @@ -61,4 +58,5 @@ class LibraryErrors: HOTP_slot_count = 3 -TOTP_slot_count = 15 \ No newline at end of file +TOTP_slot_count = 15 +PWS_SLOT_COUNT = 16 diff --git a/unittest/helpers.py b/unittest/helpers.py index 79f4e1e..90c818e 100644 --- a/unittest/helpers.py +++ b/unittest/helpers.py @@ -1,5 +1,5 @@ -def bb(x): - return bytes(x, encoding='ascii') +from constants import DeviceErrorCode, PWS_SLOT_COUNT, DefaultPasswords +from misc import gs, bb def helper_fill(str_to_fill, target_width): @@ -19,4 +19,33 @@ def helper_PWS_get_loginname(suffix): def helper_PWS_get_slotname(suffix): - return helper_fill('slotname' + suffix, 11) \ No newline at end of file + return helper_fill('slotname' + suffix, 11) + + +def helper_check_device_for_data(C): + assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK + assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK + + for i in range(0, PWS_SLOT_COUNT): + iss = str(i) + assert gs(C.NK_get_password_safe_slot_name(i)) == helper_PWS_get_slotname(iss) + assert gs(C.NK_get_password_safe_slot_login(i)) == helper_PWS_get_loginname(iss) + assert gs(C.NK_get_password_safe_slot_password(i)) == helper_PWS_get_pass(iss) + return True + + +def helper_populate_device(C): + # FIXME use object with random data, and check against it + # FIXME generate OTP as well, and check codes against its secrets + assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK + res = C.NK_enable_password_safe(DefaultPasswords.USER) + if res != DeviceErrorCode.STATUS_OK: + assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK + assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK + + for i in range(0, PWS_SLOT_COUNT): + iss = str(i) + assert C.NK_write_password_safe_slot(i, + helper_PWS_get_slotname(iss), helper_PWS_get_loginname(iss), + helper_PWS_get_pass(iss)) == DeviceErrorCode.STATUS_OK + return True diff --git a/unittest/misc.py b/unittest/misc.py index e9e1753..6a0d486 100644 --- a/unittest/misc.py +++ b/unittest/misc.py @@ -72,3 +72,7 @@ def is_long_OTP_secret_handled(C): def has_binary_counter(C): return (not is_storage(C)) or (is_storage(C) and get_devices_firmware_version(C) >= 54) + + +def bb(x): + return bytes(x, encoding='ascii') \ No newline at end of file diff --git a/unittest/test_multiple.py b/unittest/test_multiple.py index 821a3b7..96b23d7 100644 --- a/unittest/test_multiple.py +++ b/unittest/test_multiple.py @@ -28,8 +28,8 @@ from collections import defaultdict from tqdm import tqdm from conftest import skip_if_device_version_lower_than -from constants import DefaultPasswords, DeviceErrorCode, bb -from misc import gs, wait, ffi +from constants import DefaultPasswords, DeviceErrorCode +from misc import gs, wait, ffi, bb pprint = pprint.PrettyPrinter(indent=4).pprint diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 13efd28..99d7b1f 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -24,8 +24,8 @@ import pytest from conftest import skip_if_device_version_lower_than from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bbRFC_SECRET, LibraryErrors, HOTP_slot_count, \ TOTP_slot_count -from helpers import helper_PWS_get_slotname, helper_PWS_get_loginname, helper_PWS_get_pass, bb -from misc import ffi, gs, wait, cast_pointer_to_tuple, has_binary_counter +from helpers import helper_PWS_get_slotname, helper_PWS_get_loginname, helper_PWS_get_pass +from misc import ffi, gs, wait, cast_pointer_to_tuple, has_binary_counter, bb from misc import is_storage @pytest.mark.lock_device @@ -51,37 +51,21 @@ def test_write_password_safe_slot(C): @pytest.mark.PWS @pytest.mark.slowtest def test_write_all_password_safe_slots_and_read_10_times(C): - def fill(s, wid): - assert wid >= len(s) - numbers = '1234567890'*4 - s += numbers[:wid-len(s)] - assert len(s) == wid - return bb(s) - - def get_pass(suffix): - return fill('pass' + suffix, 20) - - def get_loginname(suffix): - return fill('login' + suffix, 32) - - def get_slotname(suffix): - return fill('slotname' + suffix, 11) - assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK PWS_slot_count = 16 for i in range(0, PWS_slot_count): iss = str(i) assert C.NK_write_password_safe_slot(i, - get_slotname(iss), get_loginname(iss), - get_pass(iss)) == DeviceErrorCode.STATUS_OK + helper_PWS_get_slotname(iss), helper_PWS_get_loginname(iss), + helper_PWS_get_pass(iss)) == DeviceErrorCode.STATUS_OK for j in range(0, 10): for i in range(0, PWS_slot_count): iss = str(i) - assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss) - assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss) - assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss) + assert gs(C.NK_get_password_safe_slot_name(i)) == helper_PWS_get_slotname(iss) + assert gs(C.NK_get_password_safe_slot_login(i)) == helper_PWS_get_loginname(iss) + assert gs(C.NK_get_password_safe_slot_password(i)) == helper_PWS_get_pass(iss) @pytest.mark.lock_device diff --git a/unittest/test_pro_bootloader.py b/unittest/test_pro_bootloader.py index 4a6b272..b33a9d7 100644 --- a/unittest/test_pro_bootloader.py +++ b/unittest/test_pro_bootloader.py @@ -1,7 +1,8 @@ import pytest -from conftest import skip_if_device_version_lower_than +from conftest import skip_if_device_version_lower_than, library_device_reconnect from constants import DefaultPasswords, DeviceErrorCode, LibraryErrors +from helpers import helper_populate_device, helper_check_device_for_data @pytest.mark.firmware @@ -58,16 +59,12 @@ def test_bootloader_password_change_pro_too_long(C): @pytest.mark.skip_by_default @pytest.mark.firmware -def test_bootloader_data_rention_test(C): +def test_bootloader_data_rention(C): skip_if_device_version_lower_than({'P': 11}) - def populate_device(): - return False - - def check_data_on_device(): - return False - - assert populate_device() + assert helper_populate_device(C) assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED input('Please press ENTER after uploading new firmware to the device') - assert check_data_on_device() + C = library_device_reconnect(C) + assert helper_check_device_for_data(C) + diff --git a/unittest/test_storage.py b/unittest/test_storage.py index 0f960cc..a435a15 100644 --- a/unittest/test_storage.py +++ b/unittest/test_storage.py @@ -23,8 +23,8 @@ import pprint import pytest from conftest import skip_if_device_version_lower_than -from constants import DefaultPasswords, DeviceErrorCode, bb -from misc import gs, wait, ffi +from constants import DefaultPasswords, DeviceErrorCode +from misc import gs, wait, ffi, bb pprint = pprint.PrettyPrinter(indent=4).pprint -- cgit v1.2.3 From fedf828e394938fb6f84407b4de7412a3fb6ec40 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 2 Apr 2020 15:19:00 +0200 Subject: Return serial number as uint32_t from C API This patch adds the function NK_device_serial_number_as_u32 to the C API. It is similar to NK_device_serial_number but returns the raw unsigned integer instead of a formatted string. This patch also adds a simple test case that ensures that the number is not zero. Fixes #172. --- NK_C_API.cc | 7 +++++++ NK_C_API.h | 8 ++++++++ unittest/test_pro.py | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'unittest/test_pro.py') diff --git a/NK_C_API.cc b/NK_C_API.cc index 1d3fa3a..c44e36f 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -299,6 +299,13 @@ extern "C" { }); } + NK_C_API uint32_t NK_device_serial_number_as_u32() { + auto m = NitrokeyManager::instance(); + return get_with_result([&]() { + return m->get_serial_number_as_u32(); + }); + } + NK_C_API char * NK_get_hotp_code(uint8_t slot_number) { return NK_get_hotp_code_PIN(slot_number, ""); } diff --git a/NK_C_API.h b/NK_C_API.h index d5c54a3..df3e992 100644 --- a/NK_C_API.h +++ b/NK_C_API.h @@ -385,6 +385,14 @@ extern "C" { */ NK_C_API char * NK_device_serial_number(); + /** + * Return the device's serial number string as an integer. Use + * NK_last_command_status to check for an error if this function + * returns zero. + * @return device's serial number as an integer + */ + NK_C_API uint32_t NK_device_serial_number_as_u32(); + /** * Get last command processing status. Useful for commands which returns the results of their own and could not return * an error code. diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 99d7b1f..d25a50e 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -704,6 +704,13 @@ def test_get_serial_number(C): print(('Serial number of the device: ', sn)) +@pytest.mark.status +def test_get_serial_number_as_u32(C): + sn = C.NK_device_serial_number_as_u32() + assert sn > 0 + print(('Serial number of the device (u32): ', sn)) + + @pytest.mark.otp @pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102', '00'*29+'ff', '00'*39+'ff', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ]) @@ -1038,4 +1045,4 @@ def test_OTP_all_rw(C): this_loop_codes.append(('H', i, code)) all_codes.append(this_loop_codes) from pprint import pprint - pprint(all_codes) \ No newline at end of file + pprint(all_codes) -- cgit v1.2.3 From 0270a9b3de4b45fcfcb83f8e20a78702811d4192 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 2 Apr 2020 16:29:27 +0200 Subject: Add NK_config struct and read/write functions This patch adds the NK_config struct to the C API that stores the general configuration of a Nitrokey device. It also adds the NK_read_config_struct and NK_write_config_struct functions to make the API easier to use. While NK_write_config_struct is only a convenience method, NK_read_config_struct makes the API more safe as the user no longer has to read the data from a pointer to an array. This patch also extends the test_read_write_config test case with the two new functions. --- NK_C_API.cc | 21 +++++++++++++++++++++ NK_C_API.h | 43 +++++++++++++++++++++++++++++++++++++++++++ unittest/test_pro.py | 26 +++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) (limited to 'unittest/test_pro.py') diff --git a/NK_C_API.cc b/NK_C_API.cc index 1d3fa3a..d993671 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -217,6 +217,12 @@ extern "C" { }); } + NK_C_API int NK_write_config_struct(struct NK_config config, + const char *admin_temporary_password) { + return NK_write_config(config.numlock, config.capslock, config.scrolllock, config.enable_user_password, + config.disable_user_password, admin_temporary_password); + } + NK_C_API uint8_t* NK_read_config() { auto m = NitrokeyManager::instance(); @@ -226,6 +232,21 @@ extern "C" { }); } + NK_C_API int NK_read_config_struct(struct NK_config* out) { + if (out == nullptr) { + return -1; + } + auto m = NitrokeyManager::instance(); + return get_without_result([&]() { + auto v = m->read_config(); + out->numlock = v[0]; + out->capslock = v[1]; + out->scrolllock = v[2]; + out->enable_user_password = v[3]; + out->disable_user_password = v[4]; + }); + } + NK_C_API enum NK_device_model NK_get_device_model() { auto m = NitrokeyManager::instance(); diff --git a/NK_C_API.h b/NK_C_API.h index d5c54a3..6aab7ca 100644 --- a/NK_C_API.h +++ b/NK_C_API.h @@ -265,6 +265,32 @@ extern "C" { uint8_t write_level_max; }; + /** + * The general configuration of a Nitrokey device. + */ + struct NK_config { + /** + * value in range [0-1] to send HOTP code from slot 'numlock' after double pressing numlock + * or outside the range to disable this function + */ + uint8_t numlock; + /** + * similar to numlock but with capslock + */ + uint8_t capslock; + /** + * similar to numlock but with scrolllock + */ + uint8_t scrolllock; + /** + * True to enable OTP PIN protection (require PIN each OTP code request) + */ + bool enable_user_password; + /** + * Unused. + */ + bool disable_user_password; + }; struct NK_storage_ProductionTest{ uint8_t FirmwareVersion_au8[2]; @@ -449,6 +475,14 @@ extern "C" { NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password, bool delete_user_password, const char *admin_temporary_password); + /** + * Write general config to the device + * @param config the configuration data + * @param admin_temporary_password current admin temporary password + * @return command processing error code + */ + NK_C_API int NK_write_config_struct(struct NK_config config, const char *admin_temporary_password); + /** * Get currently set config - status of function Numlock/Capslock/Scrollock OTP sending and is enabled PIN protected OTP * @see NK_write_config @@ -462,6 +496,15 @@ extern "C" { */ NK_C_API uint8_t* NK_read_config(); + /** + * Get currently set config and write it to the given pointer. + * @see NK_read_config + * @see NK_write_config_struct + * @param out a pointer to the struct that should be written to + * @return command processing error code + */ + NK_C_API int NK_read_config_struct(struct NK_config* out); + //OTP /** diff --git a/unittest/test_pro.py b/unittest/test_pro.py index 99d7b1f..e61d8bf 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -647,6 +647,30 @@ def test_read_write_config(C): config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5) assert config == (0, 1, 2, True, False) + # use structs: read I + config_st = ffi.new('struct NK_config *') + if not config_st: + raise Exception("Could not allocate config") + assert C.NK_read_config_struct(config_st) == DeviceErrorCode.STATUS_OK + assert config_st.numlock == 0 + assert config_st.capslock == 1 + assert config_st.scrolllock == 2 + assert config_st.enable_user_password + assert not config_st.disable_user_password + + # use structs: write + config_st.numlock = 3 + assert C.NK_write_config_struct(config_st[0], DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK + + # use structs: read II + err = C.NK_read_config_struct(config_st) + assert err == 0 + assert config_st.numlock == 3 + assert config_st.capslock == 1 + assert config_st.scrolllock == 2 + assert config_st.enable_user_password + assert not config_st.disable_user_password + # restore defaults and check 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 @@ -1038,4 +1062,4 @@ def test_OTP_all_rw(C): this_loop_codes.append(('H', i, code)) all_codes.append(this_loop_codes) from pprint import pprint - pprint(all_codes) \ No newline at end of file + pprint(all_codes) -- cgit v1.2.3