diff options
| -rw-r--r-- | .travis.yml | 12 | ||||
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | NK_C_API.cc | 21 | ||||
| -rw-r--r-- | NK_C_API.h | 31 | ||||
| -rw-r--r-- | unittest/conftest.py | 72 | ||||
| -rw-r--r-- | unittest/requirements.txt | 3 | ||||
| -rw-r--r-- | unittest/test_offline.py | 39 | ||||
| -rw-r--r-- | unittest/test_storage.py | 30 | 
8 files changed, 191 insertions, 19 deletions
| diff --git a/.travis.yml b/.travis.yml index bf195df..6bf5438 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,20 @@ matrix:              - cmake              - libhidapi-dev              - g++-5 +            - python3 +            - python3-pip +            - python3-requests +            - git            sources: &sources              - ubuntu-toolchain-r-test +      script: +        - make -j2 +        - ctest -VV +        - mkdir install && make install DESTDIR=install +        - pip3 install pytest --user +        - cd ../ +        - pip3 install -r unittest/requirements.txt --user +        - cd unittest && python3 -m pytest -sv test_offline.py      - os: linux        dist: trusty        env: COMPILER_NAME=gcc CXX=g++-6 CC=gcc-6 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1004baf..57ed28f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,8 +131,8 @@ execute_process(  ENDIF()  IF((NOT ${ADD_GIT_INFO}) OR (${PROJECT_VERSION_GIT_RETURN_CODE}))  	SET(PROJECT_VERSION_GIT "unknown") -	MESSAGE(STATUS "Setting Git library version to: " ${PROJECT_VERSION_GIT} )  ENDIF() +MESSAGE(STATUS "Setting Git library version to: " ${PROJECT_VERSION_GIT} )  configure_file("version.cc.in" "version.cc" @ONLY) diff --git a/NK_C_API.cc b/NK_C_API.cc index 05457d0..794673a 100644 --- a/NK_C_API.cc +++ b/NK_C_API.cc @@ -687,6 +687,23 @@ extern "C" {  		return 0;    } +	NK_C_API int NK_get_SD_usage_data(struct NK_SD_usage_data* out) { +		if (out == nullptr) +			return -1; +		auto m = NitrokeyManager::instance(); +		auto result = get_with_status([&]() { +			return m->get_SD_usage_data(); +		}, std::make_pair<uint8_t, uint8_t>(0, 0)); +		auto error_code = std::get<0>(result); +		if (error_code != 0) +			return error_code; + +		auto data = std::get<1>(result); +		out->write_level_min = std::get<0>(data); +		out->write_level_max = std::get<1>(data); + +		return 0; +	}  NK_C_API char* NK_get_SD_usage_data_as_string() {  		auto m = NitrokeyManager::instance(); @@ -697,9 +714,9 @@ NK_C_API char* NK_get_SD_usage_data_as_string() {  	NK_C_API int NK_get_progress_bar_value() {  		auto m = NitrokeyManager::instance(); -		return get_with_result([&]() { +		return std::get<1>(get_with_status([&]() {  			return m->get_progress_bar_value(); -		}); +		}, -2));  	}  	NK_C_API int NK_get_major_firmware_version() { @@ -151,6 +151,23 @@ extern "C" {  		bool stick_initialized;          }; +	/** +	 * Data about the usage of the SD card. +	 */ +	struct NK_SD_usage_data { +		/** +		 * The minimum write level, as a percentage of the total card +		 * size. +		 */ +		uint8_t write_level_min; +		/** +		 * The maximum write level, as a percentage of the total card +		 * size. +		 */ +		uint8_t write_level_max; +	}; + +     struct NK_storage_ProductionTest{      uint8_t FirmwareVersion_au8[2];      uint8_t FirmwareVersionInternal_u8; @@ -760,6 +777,17 @@ extern "C" {  	NK_C_API int NK_get_status_storage(struct NK_storage_status* out);  	/** +	 * Get SD card usage attributes. Usable during hidden volumes creation. +	 * If the command was successful (return value 0), the usage data is +	 * written to the output pointer’s target.  The output pointer must +	 * not be null. +	 * Storage only +	 * @param out the output pointer for the usage data +	 * @return command processing error code +	 */ +	NK_C_API int NK_get_SD_usage_data(struct NK_SD_usage_data* out); + +	/**  	 * Get SD card usage attributes as string.  	 * Usable during hidden volumes creation.  	 * Storage only @@ -770,7 +798,8 @@ extern "C" {  	/**  	 * Get progress value of current long operation.  	 * Storage only -	 * @return int in range 0-100 or -1 if device is not busy +	 * @return int in range 0-100 or -1 if device is not busy or -2 if an +	 *         error occured  	 */  	NK_C_API int NK_get_progress_bar_value(); diff --git a/unittest/conftest.py b/unittest/conftest.py index 253e1d8..a9f46c6 100644 --- a/unittest/conftest.py +++ b/unittest/conftest.py @@ -21,10 +21,17 @@ SPDX-License-Identifier: LGPL-3.0  import pytest -from misc import ffi +from misc import ffi, gs  device_type = None +from logging import getLogger, basicConfig, DEBUG + +basicConfig(format='* %(relativeCreated)6d %(filename)s:%(lineno)d %(message)s',level=DEBUG) +log = getLogger('conftest') +print = log.debug + +  def skip_if_device_version_lower_than(allowed_devices):      global device_type      model, version = device_type @@ -33,8 +40,45 @@ def skip_if_device_version_lower_than(allowed_devices):          pytest.skip('This device model is not applicable to run this test') +class AtrrCallProx(object): +    def __init__(self, C, name): +        self.C = C +        self.name = name + +    def __call__(self, *args, **kwargs): +        print('Calling {}{}'.format(self.name, args)) +        res = self.C(*args, **kwargs) +        res_s = res +        try: +            res_s = '{} => '.format(res) + '{}'.format(gs(res)) +        except Exception as e: +            pass +        print('Result of {}: {}'.format(self.name, res_s)) +        return res + + +class AttrProxy(object): +    def __init__(self, C, name): +        self.C = C +        self.name = name + +    def __getattr__(self, attr): +        return AtrrCallProx(getattr(self.C, attr), attr) + + +@pytest.fixture(scope="module") +def C_offline(request=None): +    print("Getting library without initializing connection") +    return get_library(request, allow_offline=True) + +  @pytest.fixture(scope="module")  def C(request=None): +    print("Getting library with connection initialized") +    return get_library(request) + + +def get_library(request, allow_offline=False):      fp = '../NK_C_API.h'      declarations = [] @@ -44,13 +88,13 @@ def C(request=None):      cnt = 0      a = iter(declarations)      for declaration in a: -        if declaration.strip().startswith('NK_C_API'): +        if declaration.strip().startswith('NK_C_API') \ +                or declaration.strip().startswith('struct'):              declaration = declaration.replace('NK_C_API', '').strip() -            while ';' not in declaration: -                declaration += (next(a)).strip() -            # print(declaration) +            while ');' not in declaration and '};' not in declaration: +                declaration += (next(a)).strip()+'\n'              ffi.cdef(declaration, override=True) -            cnt +=1 +            cnt += 1      print('Imported {} declarations'.format(cnt))      C = None @@ -82,12 +126,13 @@ def C(request=None):      nk_login = C.NK_login_auto()      if nk_login != 1:          print('No devices detected!') -    assert nk_login != 0  # returns 0 if not connected or wrong model or 1 when connected -    global device_type -    firmware_version = C.NK_get_minor_firmware_version() -    model = 'P' if firmware_version < 20 else 'S' -    device_type = (model, firmware_version) -    print('Connected device: {} {}'.format(model, firmware_version)) +    if not allow_offline: +        assert nk_login != 0  # returns 0 if not connected or wrong model or 1 when connected +        global device_type +        firmware_version = C.NK_get_minor_firmware_version() +        model = 'P' if firmware_version < 20 else 'S' +        device_type = (model, firmware_version) +        print('Connected device: {} {}'.format(model, firmware_version))      # 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 @@ -104,4 +149,5 @@ def C(request=None):      # C.NK_set_debug(True)      C.NK_set_debug_level(int(os.environ.get('LIBNK_DEBUG', 3))) -    return C +    return AttrProxy(C, "libnitrokey C") + diff --git a/unittest/requirements.txt b/unittest/requirements.txt index 5c0110b..6d718ad 100644 --- a/unittest/requirements.txt +++ b/unittest/requirements.txt @@ -1,4 +1,5 @@  cffi  pytest-repeat  pytest-randomly -oath
\ No newline at end of file +tqdm +oath diff --git a/unittest/test_offline.py b/unittest/test_offline.py new file mode 100644 index 0000000..51fe67d --- /dev/null +++ b/unittest/test_offline.py @@ -0,0 +1,39 @@ +""" +Copyright (c) 2019 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 +""" + +from misc import gs +import re + + +def test_offline(C_offline): +    C_offline.NK_set_debug(False) +    C_offline.NK_set_debug_level(4) +    assert C_offline.NK_get_major_library_version() == 3 +    assert C_offline.NK_get_minor_library_version() >= 3 +    assert C_offline.NK_login_auto() == 0 +     +    libnk_version = gs(C_offline.NK_get_library_version()) +    assert libnk_version +    print(libnk_version) + +    # v3.4.1-29-g1f3d +    search = re.search(b'v\d\.\d(\.\d)?', libnk_version) +    assert search is not None
\ No newline at end of file diff --git a/unittest/test_storage.py b/unittest/test_storage.py index 2aa8441..04b0581 100644 --- a/unittest/test_storage.py +++ b/unittest/test_storage.py @@ -24,7 +24,8 @@ import pytest  from conftest import skip_if_device_version_lower_than  from constants import DefaultPasswords, DeviceErrorCode, bb -from misc import gs, wait +from misc import gs, wait, ffi +  pprint = pprint.PrettyPrinter(indent=4).pprint @@ -367,3 +368,30 @@ def test_send_startup(C):      skip_if_device_version_lower_than({'S': 43})      time_seconds_from_epoch = 0 # FIXME set proper date      assert C.NK_send_startup(time_seconds_from_epoch) == DeviceErrorCode.STATUS_OK + + +@pytest.mark.other +def test_struct_multiline_prodtest(C): +    info_st = ffi.new('struct NK_storage_ProductionTest *') +    if info_st is None: raise Exception('Invalid value') +    err = C.NK_get_storage_production_info(info_st) +    assert err == 0 +    assert info_st.SD_Card_ManufacturingYear_u8 != 0 +    assert info_st.SD_Card_ManufacturingMonth_u8 != 0 +    assert info_st.SD_Card_Size_u8 != 0 +    assert info_st.FirmwareVersion_au8[0] == 0 +    assert info_st.FirmwareVersion_au8[1] >= 50 + +    info = 'CPU:{CPU},SC:{SC},SD:{SD},' \ +           'SCM:{SCM},SCO:{SCO},DAT:{DAT},Size:{size},Firmware:{fw} - {fwb}'.format( +        CPU='0x{:08x}'.format(info_st.CPU_CardID_u32), +        SC='0x{:08x}'.format(info_st.SmartCardID_u32), +        SD='0x{:08x}'.format(info_st.SD_CardID_u32), +        SCM='0x{:02x}'.format(info_st.SD_Card_Manufacturer_u8), +        SCO='0x{:04x}'.format(info_st.SD_Card_OEM_u16), +        DAT='20{}.{}'.format(info_st.SD_Card_ManufacturingYear_u8, info_st.SD_Card_ManufacturingMonth_u8), +        size=info_st.SD_Card_Size_u8, +        fw='{}.{}'.format(info_st.FirmwareVersion_au8[0], info_st.FirmwareVersion_au8[1]), +        fwb=info_st.FirmwareVersionInternal_u8 +        ) +    print(info) | 
