diff options
-rw-r--r-- | README.md | 83 | ||||
-rw-r--r-- | data/41-nitrokey.rules | 3 | ||||
-rw-r--r-- | data/41-nitrokey_old.rules | 3 | ||||
-rw-r--r-- | libnitrokey/device_proto.h | 2 | ||||
-rw-r--r-- | python3_bindings_example.py | 157 | ||||
-rw-r--r-- | unittest/conftest.py | 104 | ||||
-rw-r--r-- | unittest/constants.py | 14 | ||||
-rw-r--r-- | unittest/helpers.py | 51 | ||||
-rw-r--r-- | unittest/misc.py | 4 | ||||
-rw-r--r-- | unittest/test_multiple.py | 4 | ||||
-rw-r--r-- | unittest/test_pro.py | 78 | ||||
-rw-r--r-- | unittest/test_pro_bootloader.py | 71 | ||||
-rw-r--r-- | unittest/test_storage.py | 4 |
13 files changed, 457 insertions, 121 deletions
@@ -91,6 +91,7 @@ To use libnitrokey with Python a [CFFI](http://cffi.readthedocs.io/en/latest/ove pip install --user cffi # for python 2.x pip3 install cffi # for python 3.x ``` +## Python2 Just import it, read the C API header and it is done! You have access to the library. Here is an example (in Python 2) printing HOTP code for Pro or Storage device, assuming it is run in root directory [(full example)](python_bindings_example.py): ```python #!/usr/bin/env python2 @@ -158,6 +159,88 @@ print('Getting HOTP code from Nitrokey device: ') print(hotp_slot_code) libnitrokey.NK_logout() # disconnect device ``` +In case no devices are connected, a friendly message will be printed. +All available functions for C and Python are listed in [NK_C_API.h](NK_C_API.h). Please check `Documentation` section below. + +## Python3 +Just import it, read the C API header and it is done! You have access to the library. Here is an example (in Python 3) printing HOTP code for Pro or Storage device, assuming it is run in root directory [(full example)](python3_bindings_example.py): +```python +#!/usr/bin/env python3 +import cffi + +ffi = cffi.FFI() +get_string = ffi.string + +def get_library(): + fp = 'NK_C_API.h' # path to C API header + + declarations = [] + with open(fp, 'r') as f: + declarations = f.readlines() + + cnt = 0 + a = iter(declarations) + for declaration in a: + if declaration.strip().startswith('NK_C_API'): + declaration = declaration.replace('NK_C_API', '').strip() + while ';' not in declaration: + declaration += (next(a)).strip() + # print(declaration) + ffi.cdef(declaration, override=True) + cnt +=1 + print('Imported {} declarations'.format(cnt)) + + + C = None + import os, sys + path_build = os.path.join(".", "build") + paths = [ + os.environ.get('LIBNK_PATH', None), + os.path.join(path_build,"libnitrokey.so"), + os.path.join(path_build,"libnitrokey.dylib"), + os.path.join(path_build,"libnitrokey.dll"), + os.path.join(path_build,"nitrokey.dll"), + ] + for p in paths: + if not p: continue + print("Trying " +p) + p = os.path.abspath(p) + if os.path.exists(p): + print("Found: "+p) + C = ffi.dlopen(p) + break + else: + print("File does not exist: " + p) + if not C: + print("No library file found") + sys.exit(1) + + return C + + +def get_hotp_code(lib, i): + return lib.NK_get_hotp_code(i) + +def connect_device(lib): + # lib.NK_login('S'.encode('ascii')) # connect only to Nitrokey Storage device + # lib.NK_login('P'.encode('ascii')) # connect only to Nitrokey Pro device + device_connected = lib.NK_login_auto() # connect to any Nitrokey Stick + if device_connected: + print('Connected to Nitrokey device!') + else: + print('Could not connect to Nitrokey device!') + exit() + +libnitrokey = get_library() +libnitrokey.NK_set_debug(False) # do not show debug messages (log library only) + +connect_device(libnitrokey) + +hotp_slot_code = get_hotp_code(libnitrokey, 1) +print('Getting HOTP code from Nitrokey device: ') +print(ffi.string(hotp_slot_code).decode('ascii')) +libnitrokey.NK_logout() # disconnect device +``` In case no devices are connected, a friendly message will be printed. All available functions for C and Python are listed in [NK_C_API.h](NK_C_API.h). Please check `Documentation` section below. diff --git a/data/41-nitrokey.rules b/data/41-nitrokey.rules index 0099edf..52074c3 100644 --- a/data/41-nitrokey.rules +++ b/data/41-nitrokey.rules @@ -29,6 +29,9 @@ ACTION!="add|change", GOTO="u2f_end" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess" # Nitrokey FIDO U2F KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess" +# Nitrokey FIDO2 +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", TAG+="uaccess" + LABEL="u2f_end" diff --git a/data/41-nitrokey_old.rules b/data/41-nitrokey_old.rules index 1a34fa5..88e35f7 100644 --- a/data/41-nitrokey_old.rules +++ b/data/41-nitrokey_old.rules @@ -30,7 +30,8 @@ ACTION!="add|change", GOTO="u2f_end" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", MODE="0660", GROUP+="plugdev" # Nitrokey FIDO U2F KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", MODE="0660", GROUP+="plugdev" - +# Nitrokey FIDO2 +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", MODE="0660", GROUP+="plugdev" LABEL="u2f_end" diff --git a/libnitrokey/device_proto.h b/libnitrokey/device_proto.h index 45a6c16..6ffe5fb 100644 --- a/libnitrokey/device_proto.h +++ b/libnitrokey/device_proto.h @@ -249,7 +249,7 @@ namespace nitrokey { } dev->m_counters.total_comm_runs++; - int status; + int status = 0; OutgoingPacket outp; ResponsePacket resp; diff --git a/python3_bindings_example.py b/python3_bindings_example.py new file mode 100644 index 0000000..fb24eff --- /dev/null +++ b/python3_bindings_example.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +""" +Copyright (c) 2015-2018 Nitrokey UG + +This file is part of libnitrokey. + +libnitrokey is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +any later version. + +libnitrokey is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libnitrokey. If not, see <http://www.gnu.org/licenses/>. + +SPDX-License-Identifier: LGPL-3.0 +""" + +import cffi +from enum import Enum + +""" +This example will print 10 HOTP codes from just written HOTP#2 slot. +For more examples of use please refer to unittest/test_*.py files. +""" + +ffi = cffi.FFI() +get_string = ffi.string + +class DeviceErrorCode(Enum): + STATUS_OK = 0 + NOT_PROGRAMMED = 3 + WRONG_PASSWORD = 4 + STATUS_NOT_AUTHORIZED = 5 + STATUS_AES_DEC_FAILED = 0xa + + +def get_library(): + fp = 'NK_C_API.h' # path to C API header + + declarations = [] + with open(fp, 'r') as f: + declarations = f.readlines() + + cnt = 0 + a = iter(declarations) + for declaration in a: + if declaration.strip().startswith('NK_C_API'): + declaration = declaration.replace('NK_C_API', '').strip() + while ';' not in declaration: + declaration += (next(a)).strip() + # print(declaration) + ffi.cdef(declaration, override=True) + cnt +=1 + print('Imported {} declarations'.format(cnt)) + + + C = None + import os, sys + path_build = os.path.join(".", "build") + paths = [ + os.environ.get('LIBNK_PATH', None), + os.path.join(path_build,"libnitrokey.so"), + os.path.join(path_build,"libnitrokey.dylib"), + os.path.join(path_build,"libnitrokey.dll"), + os.path.join(path_build,"nitrokey.dll"), + ] + for p in paths: + if not p: continue + print("Trying " +p) + p = os.path.abspath(p) + if os.path.exists(p): + print("Found: "+p) + C = ffi.dlopen(p) + break + else: + print("File does not exist: " + p) + if not C: + print("No library file found") + print("Please set the path using LIBNK_PATH environment variable to existing library or compile it (see " + "README.md for details)") + sys.exit(1) + + return C + + +def get_hotp_code(lib, i): + return get_string(lib.NK_get_hotp_code(i)) + +def to_hex(ss): + return ''.join([ format(ord(s),'02x') for s in ss ]) + +print('Warning!') +print('This example will change your configuration on inserted stick and overwrite your HOTP#2 slot.') +print('Please write "continue" to continue or any other string to quit') +a = input() + +if not a == 'continue': + exit() + +ADMIN = input('Please enter your admin PIN (empty string uses 12345678) ') +ADMIN = ADMIN or '12345678' # use default if empty string + +show_log = input('Should log messages be shown (please write "yes" to enable; this will make harder reading script output) ') == 'yes' +libnitrokey = get_library() + +if show_log: + log_level = input('Please select verbosity level (0-5, 2 is library default, 3 will be selected on empty input) ') + log_level = log_level or '3' + log_level = int(log_level) + libnitrokey.NK_set_debug_level(log_level) +else: + libnitrokey.NK_set_debug_level(2) + + +ADMIN_TEMP = '123123123' +RFC_SECRET = to_hex('12345678901234567890') + +# libnitrokey.NK_login('S') # connect only to Nitrokey Storage device +# libnitrokey.NK_login('P') # connect only to Nitrokey Pro device +device_connected = libnitrokey.NK_login_auto() # connect to any Nitrokey Stick +if device_connected: + print('Connected to Nitrokey device!') +else: + print('Could not connect to Nitrokey device!') + exit() + +use_8_digits = True +pin_correct = libnitrokey.NK_first_authenticate(ADMIN.encode('ascii'), ADMIN_TEMP.encode('ascii')) == DeviceErrorCode.STATUS_OK.value +if pin_correct: + print('Your PIN is correct!') +else: + print('Your PIN is not correct! Please try again. Please be careful to not lock your stick!') + retry_count_left = libnitrokey.NK_get_admin_retry_count() + print('Retry count left: %d' % retry_count_left ) + exit() + +# For function parameters documentation please check NK_C_API.h +assert libnitrokey.NK_write_config(255, 255, 255, False, True, ADMIN_TEMP.encode('ascii')) == DeviceErrorCode.STATUS_OK.value +libnitrokey.NK_first_authenticate(ADMIN.encode('ascii'), ADMIN_TEMP.encode('ascii')) +libnitrokey.NK_write_hotp_slot(1, 'python_test'.encode('ascii'), RFC_SECRET.encode('ascii'), 0, use_8_digits, False, False, "".encode('ascii'), + ADMIN_TEMP.encode('ascii')) +# RFC test according to: https://tools.ietf.org/html/rfc4226#page-32 +test_data = [ + 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871, + 645520489, +] +print('Getting HOTP code from Nitrokey Stick (RFC test, 8 digits): ') +for i in range(10): + hotp_slot_1_code = get_hotp_code(libnitrokey, 1) + correct_str = "correct!" if hotp_slot_1_code.decode('ascii') == str(test_data[i])[-8:] else "not correct" + print('%d: %s, should be %s -> %s' % (i, hotp_slot_1_code.decode('ascii'), str(test_data[i])[-8:], correct_str)) +libnitrokey.NK_logout() # disconnect device diff --git a/unittest/conftest.py b/unittest/conftest.py index 49b4f02..17d9ef5 100644 --- a/unittest/conftest.py +++ b/unittest/conftest.py @@ -20,6 +20,7 @@ SPDX-License-Identifier: LGPL-3.0 """ import pytest +import os, sys from misc import ffi, gs @@ -82,47 +83,8 @@ def C(request=None): def get_library(request, allow_offline=False): - fp = '../NK_C_API.h' - - declarations = [] - with open(fp, 'r') as f: - declarations = f.readlines() - - cnt = 0 - a = iter(declarations) - for declaration in a: - if declaration.strip().startswith('NK_C_API') \ - or declaration.strip().startswith('struct'): - declaration = declaration.replace('NK_C_API', '').strip() - while ');' not in declaration and '};' not in declaration: - declaration += (next(a)).strip()+'\n' - ffi.cdef(declaration, override=True) - cnt += 1 - print('Imported {} declarations'.format(cnt)) - - C = None - import os, sys - path_build = os.path.join("..", "build") - paths = [ - os.environ.get('LIBNK_PATH', None), - os.path.join(path_build,"libnitrokey.so"), - os.path.join(path_build,"libnitrokey.dylib"), - os.path.join(path_build,"libnitrokey.dll"), - os.path.join(path_build,"nitrokey.dll"), - ] - for p in paths: - if not p: continue - print("Trying " +p) - p = os.path.abspath(p) - if os.path.exists(p): - print("Found: "+p) - C = ffi.dlopen(p) - break - else: - print("File does not exist: " + p) - if not C: - print("No library file found") - sys.exit(1) + library_read_declarations() + C = library_open_lib() C.NK_set_debug_level(int(os.environ.get('LIBNK_DEBUG', 2))) @@ -155,3 +117,63 @@ def get_library(request, allow_offline=False): return AttrProxy(C, "libnitrokey C") + +def library_open_lib(): + C = None + path_build = os.path.join("..", "build") + paths = [ + os.environ.get('LIBNK_PATH', None), + os.path.join(path_build, "libnitrokey.so"), + os.path.join(path_build, "libnitrokey.dylib"), + os.path.join(path_build, "libnitrokey.dll"), + os.path.join(path_build, "nitrokey.dll"), + ] + for p in paths: + if not p: continue + print("Trying " + p) + p = os.path.abspath(p) + if os.path.exists(p): + print("Found: " + p) + C = ffi.dlopen(p) + break + else: + print("File does not exist: " + p) + if not C: + print("No library file found") + sys.exit(1) + return C + + +def library_read_declarations(): + fp = '../NK_C_API.h' + declarations = [] + with open(fp, 'r') as f: + declarations = f.readlines() + cnt = 0 + a = iter(declarations) + for declaration in a: + if declaration.strip().startswith('NK_C_API') \ + or declaration.strip().startswith('struct'): + declaration = declaration.replace('NK_C_API', '').strip() + while ');' not in declaration and '};' not in declaration: + declaration += (next(a)).strip() + '\n' + ffi.cdef(declaration, override=True) + cnt += 1 + print('Imported {} declarations'.format(cnt)) + + +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") + + +def library_device_reconnect(C): + C.NK_logout() + C = library_open_lib() + C.NK_logout() + assert C.NK_login_auto() == 1, 'Device not found' + return C
\ No newline at end of file diff --git a/unittest/constants.py b/unittest/constants.py index 645ef6a..4047f59 100644 --- a/unittest/constants.py +++ b/unittest/constants.py @@ -18,12 +18,7 @@ along with libnitrokey. If not, see <http://www.gnu.org/licenses/>. SPDX-License-Identifier: LGPL-3.0 """ - -from misc import to_hex - -def bb(x): - return bytes(x, encoding='ascii') - +from misc import to_hex, bb RFC_SECRET_HR = '12345678901234567890' RFC_SECRET = to_hex(RFC_SECRET_HR) # '31323334353637383930...' @@ -39,6 +34,9 @@ class DefaultPasswords: USER_TEMP = b'234234234' UPDATE = b'12345678' UPDATE_TEMP = b'123update123' + UPDATE_LONG = b'1234567890'*2 + UPDATE_TOO_LONG = UPDATE_LONG + b'x' + UPDATE_TOO_SHORT = UPDATE_LONG[:7] class DeviceErrorCode: @@ -49,6 +47,7 @@ class DeviceErrorCode: STATUS_NOT_AUTHORIZED = 5 STATUS_AES_DEC_FAILED = 0xa STATUS_UNKNOWN_ERROR = 100 + STATUS_DISCONNECTED = 255 class LibraryErrors: @@ -59,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 new file mode 100644 index 0000000..90c818e --- /dev/null +++ b/unittest/helpers.py @@ -0,0 +1,51 @@ +from constants import DeviceErrorCode, PWS_SLOT_COUNT, DefaultPasswords +from misc import gs, bb + + +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) + + +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 a8df7cd..99d7b1f 100644 --- a/unittest/test_pro.py +++ b/unittest/test_pro.py @@ -22,9 +22,10 @@ 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 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 @@ -50,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 @@ -88,22 +73,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 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 @@ -111,9 +80,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 @@ -977,31 +946,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(C): - skip_if_device_version_lower_than({'P': 11}) - assert C.NK_change_firmware_password_pro(b'zxcasd', b'zxcasd') == DeviceErrorCode.WRONG_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): - skip_if_device_version_lower_than({'P': 11}) - assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD - # Not enabled due to lack of side-effect removal at this point - # assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK - - -@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.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..4cb7470 --- /dev/null +++ b/unittest/test_pro_bootloader.py @@ -0,0 +1,71 @@ +import pytest + +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 +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): + skip_if_device_version_lower_than({'P': 11}) + # 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(C): + skip_if_device_version_lower_than({'P': 11}) + + 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') + 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 |