diff options
| author | Szczepan Zalega <szczepan@nitrokey.com> | 2016-10-31 20:47:23 +0100 | 
|---|---|---|
| committer | Szczepan Zalega <szczepan@nitrokey.com> | 2016-11-26 18:56:27 +0100 | 
| commit | 103f71bb4e06132e70eb26fc2f1c5ca560068107 (patch) | |
| tree | a0e04464ec93dede63c40d407c343630974a0bf4 /unittest | |
| parent | 266b57dfe7b36799243816b7af22f3dd69b0d197 (diff) | |
| download | libnitrokey-103f71bb4e06132e70eb26fc2f1c5ca560068107.tar.gz libnitrokey-103f71bb4e06132e70eb26fc2f1c5ca560068107.tar.bz2 | |
Tests reorganization (Python)
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
Diffstat (limited to 'unittest')
| -rw-r--r-- | unittest/conftest.py | 42 | ||||
| -rw-r--r-- | unittest/constants.py | 30 | ||||
| -rw-r--r-- | unittest/misc.py | 40 | ||||
| -rw-r--r-- | unittest/test_library.py | 67 | ||||
| -rw-r--r-- | unittest/test_pro.py (renamed from unittest/test_bindings.py) | 172 | ||||
| -rw-r--r-- | unittest/test_storage.py | 7 | 
6 files changed, 189 insertions, 169 deletions
| diff --git a/unittest/conftest.py b/unittest/conftest.py new file mode 100644 index 0000000..68227d5 --- /dev/null +++ b/unittest/conftest.py @@ -0,0 +1,42 @@ +import pytest + +from misc import ffi + +@pytest.fixture(scope="module") +def C(request): +    fp = '../NK_C_API.h' + +    declarations = [] +    with open(fp, 'r') as f: +        declarations = f.readlines() + +    a = iter(declarations) +    for declaration in a: +        if declaration.startswith('extern') and not '"C"' in declaration: +            declaration = declaration.replace('extern', '').strip() +            while not ';' in declaration: +                declaration += (next(a)).strip() +            print(declaration) +            ffi.cdef(declaration, override=True) + +    C = ffi.dlopen("../build/libnitrokey.so") +    C.NK_set_debug(False) +    nk_login = C.NK_login_auto() +    if nk_login != 1: +        print('No devices detected!') +    assert nk_login == 1  # returns 0 if not connected or wrong model or 1 when connected + +    # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK +    # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK + +    # C.NK_status() + +    def fin(): +        print('\nFinishing connection to device') +        C.NK_logout() +        print('Finished') + +    request.addfinalizer(fin) +    C.NK_set_debug(True) + +    return C diff --git a/unittest/constants.py b/unittest/constants.py new file mode 100644 index 0000000..e3caae3 --- /dev/null +++ b/unittest/constants.py @@ -0,0 +1,30 @@ +from enum import Enum +from misc import to_hex + +RFC_SECRET_HR = '12345678901234567890' +RFC_SECRET = to_hex(RFC_SECRET_HR)  # '12345678901234567890' + + +# print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) ) + +class DefaultPasswords(Enum): +    ADMIN = '12345678' +    USER = '123456' +    ADMIN_TEMP = '123123123' +    USER_TEMP = '234234234' + + +class DeviceErrorCode(Enum): +    STATUS_OK = 0 +    NOT_PROGRAMMED = 3 +    WRONG_PASSWORD = 4 +    STATUS_NOT_AUTHORIZED = 5 +    STATUS_AES_DEC_FAILED = 0xa + + +class LibraryErrors(Enum): +    TOO_LONG_STRING = 200 +    INVALID_SLOT = 201 +    INVALID_HEX_STRING = 202 +    TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203 + diff --git a/unittest/misc.py b/unittest/misc.py new file mode 100644 index 0000000..b45436d --- /dev/null +++ b/unittest/misc.py @@ -0,0 +1,40 @@ +import cffi + +ffi = cffi.FFI() +gs = ffi.string + + +def to_hex(s): +    return "".join("{:02x}".format(ord(c)) for c in s) + + +def wait(t): +    import time +    msg = 'Waiting for %d seconds' % t +    print(msg.center(40, '=')) +    time.sleep(t) + + +def cast_pointer_to_tuple(obj, typen, len): +    # usage: +    #     config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5) +    return tuple(ffi.cast("%s [%d]" % (typen, len), obj)[0:len]) + +def get_firmware_version_from_status(C): +    status = gs(C.NK_status()) +    status = [s if 'firmware_version' in s else '' for s in status.split('\n')] +    firmware = status[0].split(':')[1] +    return firmware + + +def is_pro_rtm_07(C): +    firmware = get_firmware_version_from_status(C) +    return '07 00' in firmware + + +def is_storage(C): +    """ +    exact firmware storage is sent by other function +    """ +    firmware = get_firmware_version_from_status(C) +    return '01 00' in firmware
\ No newline at end of file diff --git a/unittest/test_library.py b/unittest/test_library.py new file mode 100644 index 0000000..d0eef80 --- /dev/null +++ b/unittest/test_library.py @@ -0,0 +1,67 @@ +import pytest + +from misc import ffi, gs, to_hex +from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors + +def test_too_long_strings(C): +    new_password = '123123123' +    long_string = 'a' * 100 +    assert C.NK_change_user_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_change_user_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_change_admin_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_change_admin_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_first_authenticate(long_string, DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_erase_totp_slot(0, long_string) == LibraryErrors.TOO_LONG_STRING +    digits = False +    assert C.NK_write_hotp_slot(1, long_string, RFC_SECRET, 0, digits, False, False, "", +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_write_hotp_slot(1, 'long_test', RFC_SECRET, 0, digits, False, False, "", +                                long_string) == LibraryErrors.TOO_LONG_STRING +    assert C.NK_get_hotp_code_PIN(0, long_string) == 0 +    assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING + + +def test_invalid_slot(C): +    invalid_slot = 255 +    assert C.NK_erase_totp_slot(invalid_slot, 'some password') == LibraryErrors.INVALID_SLOT +    assert C.NK_write_hotp_slot(invalid_slot, 'long_test', RFC_SECRET, 0, False, False, False, "", +                                'aaa') == LibraryErrors.INVALID_SLOT +    assert C.NK_get_hotp_code_PIN(invalid_slot, 'some password') == 0 +    assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT +    assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT +    assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK +    assert gs(C.NK_get_password_safe_slot_name(invalid_slot)) == '' +    assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT +    assert gs(C.NK_get_password_safe_slot_login(invalid_slot)) == '' +    assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT + +@pytest.mark.parametrize("invalid_hex_string", +                         ['text', '00  ', '0xff', 'zzzzzzzzzzzz', 'fff', '', 'f' * 257, 'f' * 258]) +def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string): +    """ +    Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number, +    empty or longer than 255 characters. +    """ +    assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '', +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING +    assert C.NK_write_totp_slot(1, 'python_test', invalid_hex_string, 30, True, False, False, "", +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING + + +def test_warning_binary_bigger_than_secret_buffer(C): +    invalid_hex_string = to_hex('1234567890') * 3 +    assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '', +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE + + +@pytest.mark.xfail(reason="TODO") +def test_OTP_secret_started_from_null(C): +    assert False + + +@pytest.mark.skip(reason='Experimental') +def test_clear(C): +    d = 'asdasdasd' +    print(d) +    C.clear_password(d) +    print(d)
\ No newline at end of file diff --git a/unittest/test_bindings.py b/unittest/test_pro.py index f7ade46..6ab2af9 100644 --- a/unittest/test_bindings.py +++ b/unittest/test_pro.py @@ -1,109 +1,8 @@  import pytest -import cffi -from enum import Enum - -ffi = cffi.FFI() -gs = ffi.string - - -def to_hex(s): -    return "".join("{:02x}".format(ord(c)) for c in s) - - -def wait(t): -    import time -    msg = 'Waiting for %d seconds' % t -    print(msg.center(40, '=')) -    time.sleep(t) - - -RFC_SECRET_HR = '12345678901234567890' -RFC_SECRET = to_hex(RFC_SECRET_HR)  # '12345678901234567890' - - -# print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) ) - -class DefaultPasswords(Enum): -    ADMIN = '12345678' -    USER = '123456' -    ADMIN_TEMP = '123123123' -    USER_TEMP = '234234234' - - -class DeviceErrorCode(Enum): -    STATUS_OK = 0 -    NOT_PROGRAMMED = 3 -    WRONG_PASSWORD = 4 -    STATUS_NOT_AUTHORIZED = 5 -    STATUS_AES_DEC_FAILED = 0xa - - -class LibraryErrors(Enum): -    TOO_LONG_STRING = 200 -    INVALID_SLOT = 201 -    INVALID_HEX_STRING = 202 -    TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203 - - -@pytest.fixture(scope="module") -def C(request): -    fp = '../NK_C_API.h' - -    declarations = [] -    with open(fp, 'r') as f: -        declarations = f.readlines() - -    a = iter(declarations) -    for declaration in a: -        if declaration.startswith('extern') and not '"C"' in declaration: -            declaration = declaration.replace('extern', '').strip() -            while not ';' in declaration: -                declaration += (next(a)).strip() -            print(declaration) -            ffi.cdef(declaration) - -    C = ffi.dlopen("../build/libnitrokey.so") -    C.NK_set_debug(False) -    nk_login = C.NK_login_auto() -    if nk_login != 1: -        print('No devices detected!') -    assert nk_login == 1  # returns 0 if not connected or wrong model or 1 when connected - -    # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK -    # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK - -    # C.NK_status() - -    def fin(): -        print('\nFinishing connection to device') -        C.NK_logout() -        print('Finished') - -    request.addfinalizer(fin) -    C.NK_set_debug(True) - -    return C - - -def get_firmware_version_from_status(C): -    status = gs(C.NK_status()) -    status = [s if 'firmware_version' in s else '' for s in status.split('\n')] -    firmware = status[0].split(':')[1] -    return firmware - - -def is_pro_rtm_07(C): -    firmware = get_firmware_version_from_status(C) -    return '07 00' in firmware - - -def is_storage(C): -    """ -    exact firmware storage is sent by other function -    """ -    firmware = get_firmware_version_from_status(C) -    return '01 00' in firmware +from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET +from misc import ffi, gs, wait, cast_pointer_to_tuple +from misc import is_pro_rtm_07, is_storage  def test_enable_password_safe(C):      assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK @@ -232,39 +131,6 @@ def test_user_PIN_change(C):      assert C.NK_change_user_PIN(new_password, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK -def test_too_long_strings(C): -    new_password = '123123123' -    long_string = 'a' * 100 -    assert C.NK_change_user_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_change_user_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_change_admin_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_change_admin_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_first_authenticate(long_string, DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_erase_totp_slot(0, long_string) == LibraryErrors.TOO_LONG_STRING -    digits = False -    assert C.NK_write_hotp_slot(1, long_string, RFC_SECRET, 0, digits, False, False, "", -                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_write_hotp_slot(1, 'long_test', RFC_SECRET, 0, digits, False, False, "", -                                long_string) == LibraryErrors.TOO_LONG_STRING -    assert C.NK_get_hotp_code_PIN(0, long_string) == 0 -    assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING - - -def test_invalid_slot(C): -    invalid_slot = 255 -    assert C.NK_erase_totp_slot(invalid_slot, 'some password') == LibraryErrors.INVALID_SLOT -    assert C.NK_write_hotp_slot(invalid_slot, 'long_test', RFC_SECRET, 0, False, False, False, "", -                                'aaa') == LibraryErrors.INVALID_SLOT -    assert C.NK_get_hotp_code_PIN(invalid_slot, 'some password') == 0 -    assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT -    assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT -    assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK -    assert gs(C.NK_get_password_safe_slot_name(invalid_slot)) == '' -    assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT -    assert gs(C.NK_get_password_safe_slot_login(invalid_slot)) == '' -    assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT - -  def test_admin_retry_counts(C):      default_admin_retry_count = 3      assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK @@ -609,14 +475,6 @@ def test_factory_reset(C):      assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK -@pytest.mark.skip(reason='Experimental') -def test_clear(C): -    d = 'asdasdasd' -    print(d) -    C.clear_password(d) -    print(d) - -  def test_get_status(C):      status = C.NK_status()      s = gs(status) @@ -628,27 +486,3 @@ def test_get_serial_number(C):      sn = gs(sn)      assert len(sn) > 0      print(('Serial number of the device: ', sn)) - - -@pytest.mark.parametrize("invalid_hex_string", -                         ['text', '00  ', '0xff', 'zzzzzzzzzzzz', 'fff', '', 'f' * 257, 'f' * 258]) -def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string): -    """ -    Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number, -    empty or longer than 255 characters. -    """ -    assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '', -                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING -    assert C.NK_write_totp_slot(1, 'python_test', invalid_hex_string, 30, True, False, False, "", -                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING - - -def test_warning_binary_bigger_than_secret_buffer(C): -    invalid_hex_string = to_hex('1234567890') * 3 -    assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '', -                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE - - -@pytest.mark.xfail(reason="TODO") -def test_OTP_secret_started_from_null(C): -    assert False diff --git a/unittest/test_storage.py b/unittest/test_storage.py new file mode 100644 index 0000000..d6cc558 --- /dev/null +++ b/unittest/test_storage.py @@ -0,0 +1,7 @@ +import pytest + +from misc import ffi, gs, wait, cast_pointer_to_tuple +from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors + + + | 
