Skip to content

Commit

Permalink
Update tests for Nitrokey Storage
Browse files Browse the repository at this point in the history
Add tests for Nitrokey Storage exploring invalid data for HV setup
  • Loading branch information
szszszsz committed Mar 4, 2022
1 parent da91e37 commit 03303c8
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 4 deletions.
1 change: 1 addition & 0 deletions unittest/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pytest-repeat
pytest-randomly
tqdm
oath
hypothesis
4 changes: 2 additions & 2 deletions unittest/test_pro.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,13 +673,13 @@ def test_read_write_config(C):
assert not config_st.disable_user_password

# use structs: write
config_st.numlock = 3
config_st.numlock = 0xFF
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.numlock == 0xFF
assert config_st.capslock == 1
assert config_st.scrolllock == 2
assert config_st.enable_user_password
Expand Down
107 changes: 105 additions & 2 deletions unittest/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@
SPDX-License-Identifier: LGPL-3.0
"""

import dataclasses
import pprint
import secrets
from datetime import datetime, timedelta
from time import sleep

import pytest
from hypothesis import given, strategies as st, settings, Verbosity, assume

from conftest import skip_if_device_version_lower_than
from constants import DefaultPasswords, DeviceErrorCode
Expand All @@ -43,6 +48,15 @@ def get_dict_from_dissect(status):
return d


def get_status_storage(C):
status_pointer = C.NK_get_status_storage_as_string()
assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
status_string = gs(status_pointer)
assert len(status_string) > 0
status_dict = get_dict_from_dissect(status_string.decode('ascii'))
# assert int(status_dict['AdminPwRetryCount']) == default_admin_password_retry_count
return status_dict, C.NK_get_last_command_status()

@pytest.mark.other
@pytest.mark.info
def test_get_status_storage(C):
Expand Down Expand Up @@ -572,8 +586,97 @@ def test_export_firmware_extended_macos(C):
assert checks_add == checks


def skip_if_not_macos(message:str) -> None:
def skip_if_not_macos(message: str) -> None:
import platform

if platform.system() != 'Darwin':
pytest.skip(message)


from typing import Callable, Optional


def helper_busy_wait(func: Callable, timeout=10) -> DeviceErrorCode:
start = datetime.now()
res = func()
while res == DeviceErrorCode.BUSY:
sleep(.5)
status_dict, res = get_status_storage(C)
print(status_dict)
if datetime.now() - start > timedelta(seconds=timeout):
print('Timeout, stopping')
return res
return res


def helper_create_hidden_volume(C, slot, begin, end, password_in: Optional[bytes], should_work=True):
"""
:param password_in: if None, generate randomly per call
"""
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK

password = password_in
if password_in is None:
password = secrets.token_hex(5)
password = password.encode('ascii')
assert not should_work ^ (helper_busy_wait(
lambda: C.NK_create_hidden_volume(slot, begin, end, password)) == DeviceErrorCode.STATUS_OK)

assert not should_work ^ (
helper_busy_wait(lambda: C.NK_unlock_hidden_volume(password)) == DeviceErrorCode.STATUS_OK)
if should_work:
assert helper_busy_wait(lambda: C.NK_lock_hidden_volume()) == DeviceErrorCode.STATUS_OK


@dataclasses.dataclass
class MalformedParams:
slot: int
begin: int
end: int
password: Optional[bytes] = None
should_work: bool = True

def as_list(self):
return [self.slot, self.begin, self.end, self.password, self.should_work]

@pytest.mark.hidden
@pytest.mark.parametrize("args", [
# correct calls
MalformedParams(slot=0, begin=1, end=2),
MalformedParams(slot=0, begin=0, end=100),
MalformedParams(slot=0, begin=99, end=100),
MalformedParams(slot=0, begin=0, end=1),
MalformedParams(slot=0, begin=0, end=1),
# password not empty
# password not longer than 20 characters
MalformedParams(slot=0, begin=1, end=2, password=b"", should_work=False),
MalformedParams(slot=0, begin=1, end=2, password=b"a" * 21, should_work=False),
MalformedParams(slot=0, begin=1, end=2, password=b"a" * 20),
# slot 0..3
MalformedParams(slot=5, begin=1, end=2, should_work=False),
MalformedParams(slot=55, begin=1, end=2, should_work=False),
# end 1..100
MalformedParams(slot=0, begin=0, end=101, should_work=False),
# begin 0..99
MalformedParams(slot=0, begin=100, end=100, should_work=False),
MalformedParams(slot=0, begin=50, end=49, should_work=False),
MalformedParams(slot=0, begin=50, end=50, should_work=False),
], ids=str)
def test_hidden_volume_malformed_parameters(C, args):
skip_if_device_version_lower_than({'S': 56})
helper_create_hidden_volume(C, *args.as_list())


# password=st.text(min_size=10, max_size=20) # disabled to avoid failed tests due to unlocking previously correctly
# configured HV with the same password used in failing example
@given(slot=st.integers(min_value=0, max_value=3), begin=st.integers(min_value=0, max_value=99),
end=st.integers(min_value=1, max_value=100))
@settings(max_examples=15, deadline=timedelta(seconds=10), verbosity=Verbosity.verbose)
def test_hypo_hidden_volume_setup(C, slot, begin, end):
assume(begin < end)
# password = password.encode('utf-8')
password = None
helper_create_hidden_volume(C, slot, begin, end, password, should_work=True)

0 comments on commit 03303c8

Please sign in to comment.