├── .Dockerignore ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── .idea ├── codeStyleSettings.xml ├── dictionaries │ └── sz.xml └── vcs.xml ├── .travis.yml ├── CHANGELOG.md ├── CMakeLists.txt ├── CMakeSettings.json ├── DeviceCommunicationExceptions.cpp ├── Dockerfile ├── Doxyfile.in ├── LICENSE ├── Makefile ├── NK_C_API.cc ├── NK_C_API.h ├── NitrokeyManager.cc ├── README.md ├── TODO ├── ci-script ├── build.sh └── package.sh ├── command_id.cc ├── data ├── 41-nitrokey.rules ├── 41-nitrokey_old.rules ├── Pipfile ├── Pipfile.lock ├── README.md └── generate_udev_docs.py ├── device.cc ├── libnitrokey.pc.in ├── libnitrokey.pro ├── libnitrokey ├── CommandFailedException.h ├── DeviceCommunicationExceptions.h ├── LibraryException.h ├── LongOperationInProgressException.h ├── NitrokeyManager.h ├── command.h ├── command_id.h ├── cxx_semantics.h ├── deprecated.h ├── device.h ├── device_proto.h ├── dissect.h ├── hidapi │ └── hidapi.h ├── log.h ├── misc.h ├── stick10_commands.h ├── stick10_commands_0.8.h ├── stick20_commands.h └── version.h ├── log.cc ├── meson.build ├── meson_options.txt ├── misc.cc ├── python3_bindings_example.py ├── python_bindings_example.py ├── unittest ├── Pipfile ├── Pipfile.lock ├── build │ ├── libnitrokey.so │ └── run.sh ├── catch_main.cpp ├── conftest.py ├── constants.py ├── helpers.py ├── libnk-tool.py ├── misc.py ├── pyproject.toml ├── requirements.txt ├── setup_python_dependencies.sh ├── test1.cc ├── test2.cc ├── test3.cc ├── test_C_API.cpp ├── test_HOTP.cc ├── test_command_ids_header.h ├── test_issues.cc ├── test_issues.py ├── test_library.py ├── test_memory.c ├── test_minimal.c ├── test_multiple.py ├── test_multiple_devices.cc ├── test_offline.cc ├── test_offline.py ├── test_pro.py ├── test_pro_bootloader.py ├── test_safe.cpp ├── test_storage.py └── test_strdup.cpp ├── version.cc └── version.cc.in /.Dockerignore: -------------------------------------------------------------------------------- 1 | build* 2 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | *.log 3 | *.o 4 | build 5 | unittest/build/ 6 | unittest/.pytest_cache/ 7 | *.pyc 8 | core 9 | .cache/ 10 | .idea/ 11 | CMakeFiles/ 12 | /.vs 13 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 'https://raw.githubusercontent.com/Nitrokey/common-ci-jobs/master/common_jobs.yml' 2 | 3 | stages: 4 | - pull-github 5 | - fetch 6 | - build 7 | - deploy 8 | 9 | variables: 10 | #Repo for shared scripts (pull.sh release.sh, nightly_upload.sh): 11 | GIT_STRATEGY: clone #This seems to have no effect also set in webinterface 12 | GIT_DEPTH: 0 #This seems to have no effect also set in webinterface 13 | GIT_SUBMODULE_STRATEGY: recursive #This seems to have no effect also set in webinterfac 14 | SCRIPTS_REPO: git@git.dotplex.com:nitrokey/gitlab-ci.git 15 | REPO_USER: nitrokey 16 | REPO_NAME: libnitrokey 17 | MAIN_BRANCH: master 18 | COMMON_PULL: "true" 19 | COMMON_UPLOAD_NIGHTLY: "true" 20 | COMMON_GITHUB_RELEASE: "true" 21 | COMMON_UPLOAD_FILES: "false" 22 | 23 | fetch-and-package: 24 | image: registry.git.dotplex.com/nitrokey/libnitrokey/libnitrokey-bionic-gcc8:latest 25 | rules: 26 | - if: '$CI_PIPELINE_SOURCE == "push"' 27 | - if: '$CI_PIPELINE_SOURCE == "schedule"' 28 | - if: '$CI_PIPELINE_SOURCE == "web"' 29 | 30 | tags: 31 | - docker 32 | stage: fetch 33 | script: 34 | - ci-script/package.sh 35 | after_script: 36 | - wget $icon_server/checkmark/$CI_COMMIT_REF_NAME/$CI_COMMIT_SHA/$CI_JOB_NAME/$CI_JOB_STATUS/${CI_JOB_URL#*/*/*/} 37 | artifacts: 38 | paths: 39 | - artifacts 40 | - libnitrokey-source-metadata 41 | expire_in: 2 weeks 42 | 43 | .build: 44 | rules: 45 | - if: '$CI_PIPELINE_SOURCE == "push"' 46 | - if: '$CI_PIPELINE_SOURCE == "schedule"' 47 | - if: '$CI_PIPELINE_SOURCE == "web"' 48 | tags: 49 | - docker 50 | stage: build 51 | script: 52 | - ci-script/build.sh 53 | after_script: 54 | - wget $icon_server/checkmark/$CI_COMMIT_REF_NAME/$CI_COMMIT_SHA/$CI_JOB_NAME/$CI_JOB_STATUS/${CI_JOB_URL#*/*/*/} 55 | 56 | build-bionic-gcc8: 57 | extends: .build 58 | image: registry.git.dotplex.com/nitrokey/libnitrokey/libnitrokey-bionic-gcc8:latest 59 | 60 | build-bionic-gcc7: 61 | extends: .build 62 | image: registry.git.dotplex.com/nitrokey/libnitrokey/libnitrokey-bionic-gcc7:latest 63 | 64 | build-bionic-gcc6: 65 | extends: .build 66 | image: registry.git.dotplex.com/nitrokey/libnitrokey/libnitrokey-bionic-gcc6:latest 67 | 68 | build-bionic-gcc5: 69 | extends: .build 70 | image: registry.git.dotplex.com/nitrokey/libnitrokey/libnitrokey-bionic-gcc5:latest 71 | 72 | build-bionic-llvm7: 73 | extends: .build 74 | image: registry.git.dotplex.com/nitrokey/libnitrokey/libnitrokey-bionic-llvm7:latest 75 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "unittest/Catch"] 2 | path = unittest/Catch 3 | url = https://github.com/catchorg/Catch2.git 4 | [submodule "hidapi"] 5 | path = hidapi 6 | url = https://github.com/Nitrokey/hidapi.git 7 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/dictionaries/sz.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | loglevel 5 | nitrokey 6 | totp 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | os: osx 3 | env: 4 | global: 5 | - CF="-DCOMPILE_OFFLINE_TESTS=1 -DERROR_ON_WARNING=ON" 6 | 7 | jobs: 8 | include: 9 | - osx_image: xcode11.5 10 | - osx_image: xcode9.1 11 | - os: linux 12 | dist: trusty 13 | env: COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 14 | addons: 15 | apt: 16 | packages: 17 | - cmake 18 | - libhidapi-dev 19 | - g++-5 20 | sources: &sources 21 | - ubuntu-toolchain-r-test 22 | - os: linux 23 | dist: trusty 24 | env: COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 25 | addons: 26 | apt: 27 | packages: 28 | - cmake 29 | - libhidapi-dev 30 | - g++-7 31 | sources: *sources 32 | - os: linux 33 | dist: bionic 34 | env: COMPILER_NAME=gcc CXX=g++-10 CC=gcc-10 35 | addons: 36 | apt: 37 | packages: 38 | - cmake 39 | - libhidapi-dev 40 | - g++-10 41 | - python3 42 | - python3-pip 43 | - meson 44 | - ninja-build 45 | sources: *sources 46 | script: 47 | - make -j2 48 | - ctest -VV 49 | - mkdir install && make install DESTDIR=install 50 | - cd ../ 51 | - python3 -m pip install -r unittest/requirements.txt --user 52 | - cd unittest && python3 -m pytest -sv test_offline.py 53 | - cd ../ 54 | - mkdir -p build-meson && meson build-meson 55 | - cd build-meson && ninja 56 | - env DESTDIR=install ninja install 57 | - os: linux 58 | dist: trusty 59 | env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8 60 | addons: 61 | apt: 62 | packages: 63 | - cmake 64 | - libhidapi-dev 65 | - g++-5 66 | - clang-3.8 67 | sources: *sources 68 | - os: linux 69 | dist: bionic 70 | env: COMPILER_NAME=clang CXX=clang++-6.0 CC=clang-6.0 71 | addons: 72 | apt: 73 | packages: 74 | - cmake 75 | - libhidapi-dev 76 | - clang-6.0 77 | sources: *sources 78 | - os: linux 79 | dist: bionic 80 | env: COMPILER_NAME=clang CXX=clang++-9 CC=clang-9 81 | addons: 82 | apt: 83 | packages: 84 | - cmake 85 | - libhidapi-dev 86 | - clang-9 87 | sources: *sources 88 | 89 | 90 | install: 91 | - mkdir -p build 92 | - cd build 93 | # - export CXXFLAGS="${CXX_FLAGS} -Wall -Wextra -Werror" # TODO enable when fixed 94 | - ${CXX} --version || true 95 | - cmake --version 96 | - cmake .. ${CF} 97 | 98 | script: 99 | - make -j2 100 | - ctest -VV 101 | - mkdir install && make install DESTDIR=install -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v3.2](https://github.com/Nitrokey/libnitrokey/tree/v3.2) (2018-01-16) 4 | [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v3.1...v3.2) 5 | 6 | **Closed issues:** 7 | 8 | - Communication exceptions are not catched in C API [\#89](https://github.com/Nitrokey/libnitrokey/issues/89) 9 | - Devices are not detected under Windows [\#88](https://github.com/Nitrokey/libnitrokey/issues/88) 10 | - Python bindings example is not working [\#82](https://github.com/Nitrokey/libnitrokey/issues/82) 11 | - Missing NK\_C\_API.h in install includes [\#77](https://github.com/Nitrokey/libnitrokey/issues/77) 12 | - Handle time-taking commands [\#67](https://github.com/Nitrokey/libnitrokey/issues/67) 13 | 14 | ## [v3.1](https://github.com/Nitrokey/libnitrokey/tree/v3.1) (2017-10-11) 15 | [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v3.0...v3.1) 16 | 17 | ## [v3.0](https://github.com/Nitrokey/libnitrokey/tree/v3.0) (2017-10-07) 18 | [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v2.0...v3.0) 19 | 20 | **Implemented enhancements:** 21 | 22 | - Support for NK Pro 0.8 [\#51](https://github.com/Nitrokey/libnitrokey/issues/51) 23 | 24 | **Closed issues:** 25 | 26 | - tests are failing [\#71](https://github.com/Nitrokey/libnitrokey/issues/71) 27 | - cmake doesn't install any headers [\#70](https://github.com/Nitrokey/libnitrokey/issues/70) 28 | - SONAME versioning [\#69](https://github.com/Nitrokey/libnitrokey/issues/69) 29 | - Read slot returns invalid counter value for Storage [\#59](https://github.com/Nitrokey/libnitrokey/issues/59) 30 | - Return OTP codes as strings [\#57](https://github.com/Nitrokey/libnitrokey/issues/57) 31 | - Fix compilation warnings [\#56](https://github.com/Nitrokey/libnitrokey/issues/56) 32 | - Add Travis support [\#48](https://github.com/Nitrokey/libnitrokey/issues/48) 33 | - Correct get\_time [\#27](https://github.com/Nitrokey/libnitrokey/issues/27) 34 | - Move from Makefile to CMake [\#18](https://github.com/Nitrokey/libnitrokey/issues/18) 35 | 36 | ## [v2.0](https://github.com/Nitrokey/libnitrokey/tree/v2.0) (2016-12-12) 37 | [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v1.0...v2.0) 38 | 39 | **Implemented enhancements:** 40 | 41 | - Support for Nitrokey Storage - Nitrokey Storage commands [\#14](https://github.com/Nitrokey/libnitrokey/issues/14) 42 | - Support for Nitrokey Storage - Nitrokey Pro commands [\#13](https://github.com/Nitrokey/libnitrokey/issues/13) 43 | 44 | **Fixed bugs:** 45 | 46 | - Fails to compile on ubuntu 16.04 [\#46](https://github.com/Nitrokey/libnitrokey/issues/46) 47 | - C++ tests do not compile [\#45](https://github.com/Nitrokey/libnitrokey/issues/45) 48 | - HOTP counter values limited to 8bit [\#44](https://github.com/Nitrokey/libnitrokey/issues/44) 49 | - Device is not released after library function disconnect call [\#43](https://github.com/Nitrokey/libnitrokey/issues/43) 50 | 51 | **Closed issues:** 52 | 53 | - Compilation error on G++6 \(flexible array member\) [\#49](https://github.com/Nitrokey/libnitrokey/issues/49) 54 | - No library function for getting device's serial number [\#33](https://github.com/Nitrokey/libnitrokey/issues/33) 55 | - Pass binary data \(OTP secrets\) coded as hex values [\#31](https://github.com/Nitrokey/libnitrokey/issues/31) 56 | - set\_time not always work [\#29](https://github.com/Nitrokey/libnitrokey/issues/29) 57 | 58 | **Merged pull requests:** 59 | 60 | - Support for Nitrokey Pro 0.8 [\#53](https://github.com/Nitrokey/libnitrokey/pull/53) ([szszszsz](https://github.com/szszszsz)) 61 | - Support Nitrokey Storage [\#52](https://github.com/Nitrokey/libnitrokey/pull/52) ([szszszsz](https://github.com/szszszsz)) 62 | - Fix compilation G++6 error [\#50](https://github.com/Nitrokey/libnitrokey/pull/50) ([szszszsz](https://github.com/szszszsz)) 63 | - Fix compilation warning and error under G++ [\#47](https://github.com/Nitrokey/libnitrokey/pull/47) ([szszszsz](https://github.com/szszszsz)) 64 | - Support Pro stick commands on Storage device [\#42](https://github.com/Nitrokey/libnitrokey/pull/42) ([szszszsz](https://github.com/szszszsz)) 65 | - Readme update - dependencies, compilers, format [\#40](https://github.com/Nitrokey/libnitrokey/pull/40) ([szszszsz](https://github.com/szszszsz)) 66 | - Issue 31 secret as hex [\#36](https://github.com/Nitrokey/libnitrokey/pull/36) ([szszszsz](https://github.com/szszszsz)) 67 | - Function for getting device's serial number in hex. Fixes \#33 [\#34](https://github.com/Nitrokey/libnitrokey/pull/34) ([szszszsz](https://github.com/szszszsz)) 68 | 69 | ## [v1.0](https://github.com/Nitrokey/libnitrokey/tree/v1.0) (2016-08-09) 70 | [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v0.9...v1.0) 71 | 72 | **Closed issues:** 73 | 74 | - Automatically detect any connected stick [\#22](https://github.com/Nitrokey/libnitrokey/issues/22) 75 | - Security check [\#20](https://github.com/Nitrokey/libnitrokey/issues/20) 76 | - Show friendly message when no device is connected and do not abort [\#17](https://github.com/Nitrokey/libnitrokey/issues/17) 77 | - Fix PIN protected OTP for Pro [\#15](https://github.com/Nitrokey/libnitrokey/issues/15) 78 | 79 | ## [v0.9](https://github.com/Nitrokey/libnitrokey/tree/v0.9) (2016-08-05) 80 | [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v0.8...v0.9) 81 | 82 | **Closed issues:** 83 | 84 | - Add README [\#6](https://github.com/Nitrokey/libnitrokey/issues/6) 85 | - Support configs for OTP slots [\#19](https://github.com/Nitrokey/libnitrokey/issues/19) 86 | - Cover remaining Nitrokey Pro commands in lib and tests [\#16](https://github.com/Nitrokey/libnitrokey/issues/16) 87 | 88 | **Merged pull requests:** 89 | 90 | - waffle.io Badge [\#21](https://github.com/Nitrokey/libnitrokey/pull/21) ([waffle-iron](https://github.com/waffle-iron)) 91 | 92 | ## [v0.8](https://github.com/Nitrokey/libnitrokey/tree/v0.8) (2016-08-02) 93 | **Implemented enhancements:** 94 | 95 | - Change license to LGPLv3 [\#1](https://github.com/Nitrokey/libnitrokey/issues/1) 96 | 97 | **Closed issues:** 98 | 99 | - Python wrapper for OTP and PINs [\#12](https://github.com/Nitrokey/libnitrokey/issues/12) 100 | - Fails to built with default setting on ubuntu 14.04 [\#11](https://github.com/Nitrokey/libnitrokey/issues/11) 101 | - Check why packet buffers are not zeroed [\#8](https://github.com/Nitrokey/libnitrokey/issues/8) 102 | 103 | **Merged pull requests:** 104 | 105 | - Reset the HOTP counter [\#9](https://github.com/Nitrokey/libnitrokey/pull/9) ([cornelinux](https://github.com/cornelinux)) 106 | 107 | 108 | 109 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file. 3 | "configurations": [ 4 | { 5 | "name": "x86-Debug", 6 | "generator": "Visual Studio 15 2017", 7 | "configurationType" : "Debug", 8 | "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", 9 | "cmakeCommandArgs": "", 10 | "buildCommandArgs": "-m -v:minimal" 11 | }, 12 | { 13 | "name": "x86-Release", 14 | "generator": "Visual Studio 15 2017", 15 | "configurationType" : "Release", 16 | "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", 17 | "cmakeCommandArgs": "", 18 | "buildCommandArgs": "-m -v:minimal" 19 | }, 20 | { 21 | "name": "x64-Debug", 22 | "generator": "Visual Studio 15 2017 Win64", 23 | "configurationType" : "Debug", 24 | "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", 25 | "cmakeCommandArgs": "", 26 | "buildCommandArgs": "-m -v:minimal" 27 | }, 28 | { 29 | "name": "x64-Release", 30 | "generator": "Visual Studio 15 2017 Win64", 31 | "configurationType" : "Release", 32 | "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", 33 | "cmakeCommandArgs": "", 34 | "buildCommandArgs": "-m -v:minimal" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /DeviceCommunicationExceptions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "DeviceCommunicationExceptions.h" 23 | 24 | std::atomic_int DeviceCommunicationException::occurred {0}; 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | MAINTAINER Nitrokey 3 | 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt update && apt install -qy --no-install-recommends \ 6 | libhidapi-dev libusb-1.0-0-dev cmake gcc g++ make doxygen pkg-config alien git graphviz \ 7 | build-essential:native pkg-kde-tools udev gnupg curl devscripts \ 8 | debian-keyring ubuntu-keyring \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | RUN mkdir -p /app /root/.gnupg 12 | 13 | WORKDIR /app 14 | -------------------------------------------------------------------------------- /Doxyfile.in: -------------------------------------------------------------------------------- 1 | # For better readability, the documentation and default settings are removed 2 | # from this file. For more information on the Doxygen configuration, generate 3 | # a new Doxyfile using `doxygen -g` or have a look at the Doxygen manual at 4 | # http://www.doxygen.nl/manual/config.html 5 | # 6 | # This file is processed by CMake. To generate the documentation, run `make 7 | # doc` in the build directory. 8 | 9 | PROJECT_NAME = "@CMAKE_PROJECT_NAME@" 10 | PROJECT_NUMBER = "@PROJECT_VERSION@" 11 | PROJECT_BRIEF = 12 | OUTPUT_DIRECTORY = doc 13 | STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@ 14 | JAVADOC_AUTOBRIEF = YES 15 | OPTIMIZE_OUTPUT_FOR_C = YES 16 | WARN_NO_PARAMDOC = YES 17 | INPUT = @CMAKE_CURRENT_SOURCE_DIR@/NK_C_API.h 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NPROC=$(shell nproc) 2 | IMAGE_NAME=libnk-build 3 | BUILD_DIR=build 4 | 5 | .PHONY: docker-build-all 6 | docker-build-all: 7 | @echo "Running isolated build" 8 | $(MAKE) docker-build-image 9 | $(MAKE) docker-build 10 | 11 | .PHONY: docker-build-image 12 | docker-build-image: 13 | sudo docker build -t $(IMAGE_NAME) . 14 | 15 | .PHONY: docker-build 16 | docker-build: 17 | sudo docker run -it --rm -v $(PWD):/app $(IMAGE_NAME) bash -c "make ci-build" 18 | 19 | DOCKERCMD=bash -c "make ci-package" 20 | .PHONY: docker-package 21 | docker-package: 22 | sudo docker run -it --rm -v $(PWD):/app $(IMAGE_NAME) $(DOCKERCMD) 23 | 24 | .PHONY: docker-clean 25 | docker-clean: 26 | cd $(BUILD_DIR) && $(MAKE) clean 27 | sudo docker rmi $(IMAGE_NAME) --force 28 | 29 | .PHONY: ci-build 30 | ci-build: 31 | mkdir -p $(BUILD_DIR) && rm -rf $(BUILD_DIR)/* 32 | cd $(BUILD_DIR) && cmake .. && $(MAKE) -j$(NPROC) package 33 | cd $(BUILD_DIR) && ctest -VV 34 | cd $(BUILD_DIR) && mkdir -p install && $(MAKE) install DESTDIR=install 35 | @echo "== Results available in $(BUILD_DIR)" 36 | @date 37 | 38 | DGET_URL=https://people.debian.org/~patryk/tmp/libnitrokey/libnitrokey_3.7-1.dsc 39 | .PHONY: ci-package 40 | ci-package: 41 | mkdir -p $(BUILD_DIR) && rm -rf $(BUILD_DIR)/* 42 | cd $(BUILD_DIR) && dget $(DGET_URL) 43 | cd $(BUILD_DIR) && cd libnitrokey-* && dpkg-buildpackage 44 | 45 | .PHONY: ci-tests 46 | ci-tests: 47 | pip install pytest --user 48 | pip install -r unittest/requirements.txt --user 49 | cd unittest && python3 -m pytest -sv test_offline.py 50 | 51 | REPORT_NAME=libnitrokey-tests-report.html 52 | PYTEST_ARG=-vx --randomly-dont-reorganize --template=html1/index.html --report=$(REPORT_NAME) 53 | 54 | tests-setup: 55 | cd unittest && pipenv --python $(shell which python3) && pipenv install --dev 56 | 57 | .PHONY: tests-pro 58 | tests-pro: 59 | cd unittest && pipenv run pytest $(PYTEST_ARG) test_pro.py ; xdg-open $(REPORT_NAME) 60 | 61 | .PHONY: tests-storage 62 | tests-storage: 63 | cd unittest && pipenv run pytest $(PYTEST_ARG) test_pro.py 64 | cd unittest && pipenv run pytest $(PYTEST_ARG) test_storage.py ; xdg-open $(REPORT_NAME) 65 | 66 | .PHONY: clean clean-all 67 | clean: 68 | -rm $(REPORT_NAME) 69 | 70 | clean-all: clean docker-clean -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | use strings instead of char* and vectors instead of others 2 | -------------------------------------------------------------------------------- /ci-script/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -exuo pipefail 3 | export 4 | 5 | . ./libnitrokey-source-metadata/metadata 6 | tar xf artifacts/${LIBNITROKEY_BUILD_OUTNAME}.tar.gz 7 | 8 | 9 | pushd ${LIBNITROKEY_BUILD_OUTNAME} 10 | pip3 install --user -r unittest/requirements.txt 11 | 12 | ## This is quite sketchy but will work for now - we're using a tarball prepared by git archive, so we can't pull submodules. 13 | ## Instead, we download the Catch2 header manually. The version is hardcoded, which is bad. 14 | ## TODO: figure out a better way to handle that 15 | ## One possibility is to install Catch2 system-wide on builder images. 16 | mkdir -p unittest/Catch/single_include/catch2 17 | curl -L -o unittest/Catch/single_include/catch2/catch.hpp https://github.com/catchorg/Catch2/releases/download/v2.3.0/catch.hpp 18 | 19 | mkdir build 20 | mkdir install 21 | 22 | pushd build 23 | cmake .. -DERROR_ON_WARNING=OFF -DCOMPILE_TESTS=ON 24 | make -j2 25 | ctest -VV 26 | make install DESTDIR=../install 27 | popd 28 | 29 | pushd unittest 30 | python3 -m pytest -sv test_offline.py 31 | popd 32 | -------------------------------------------------------------------------------- /ci-script/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -exuo pipefail 3 | export 4 | mkdir -p artifacts 5 | OUTDIR="$(realpath artifacts)" 6 | 7 | BASENAME="libnitrokey" 8 | #pushd libnitrokey 9 | 10 | VERSION="$(git describe --abbrev=0)" 11 | BUILD="${VERSION}.${CI_COMMIT_SHORT_SHA}" 12 | DATE="$(date -Iseconds)" 13 | case "${CI_PIPELINE_SOURCE}" in 14 | push) 15 | OUTNAME="${BASENAME}-${BUILD}" 16 | ;; 17 | schedule) 18 | OUTNAME="${BASENAME}-${DATE}" 19 | ;; 20 | web) 21 | OUTNAME="${BASENAME}-${VERSION}" 22 | ;; 23 | esac 24 | 25 | git archive --format tar --prefix ${OUTNAME}/ ${CI_COMMIT_SHA} | gzip > ${OUTDIR}/${OUTNAME}.tar.gz 26 | echo size: 27 | gzip -l ${OUTDIR}/${OUTNAME}.tar.gz 28 | 29 | echo "LIBNITROKEY_BUILD_VERSION=\"${VERSION}\"" >> ./metadata 30 | echo "LIBNITROKEY_BUILD_ID=\"${BUILD}\"" >> ./metadata 31 | echo "LIBNITROKEY_BUILD_DATE=\"${DATE}\"" >> ./metadata 32 | echo "LIBNITROKEY_BUILD_TYPE=\"${CI_PIPELINE_SOURCE}\"" >> ./metadata 33 | echo "LIBNITROKEY_BUILD_OUTNAME=\"${OUTNAME}\"" >> ./metadata 34 | cat ./metadata 35 | pwd 36 | ls 37 | mkdir -p libnitrokey-source-metadata 38 | mv metadata libnitrokey-source-metadata/ 39 | cat libnitrokey-source-metadata/metadata 40 | pushd ${OUTDIR} 41 | sha256sum *.tar.gz > SHA256SUM 42 | popd 43 | -------------------------------------------------------------------------------- /command_id.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include 23 | #include "command_id.h" 24 | 25 | namespace nitrokey { 26 | namespace proto { 27 | 28 | const char *commandid_to_string(CommandID id) { 29 | #ifdef NO_LOG 30 | return ""; 31 | #endif 32 | switch (id) { 33 | case CommandID::GET_STATUS: 34 | return "GET_STATUS"; 35 | case CommandID::WRITE_TO_SLOT: 36 | return "WRITE_TO_SLOT"; 37 | case CommandID::READ_SLOT_NAME: 38 | return "READ_SLOT_NAME"; 39 | case CommandID::READ_SLOT: 40 | return "READ_SLOT"; 41 | case CommandID::GET_CODE: 42 | return "GET_CODE"; 43 | case CommandID::WRITE_CONFIG: 44 | return "WRITE_CONFIG"; 45 | case CommandID::ERASE_SLOT: 46 | return "ERASE_SLOT"; 47 | case CommandID::FIRST_AUTHENTICATE: 48 | return "FIRST_AUTHENTICATE"; 49 | case CommandID::AUTHORIZE: 50 | return "AUTHORIZE"; 51 | case CommandID::GET_PASSWORD_RETRY_COUNT: 52 | return "GET_PASSWORD_RETRY_COUNT"; 53 | case CommandID::CLEAR_WARNING: 54 | return "CLEAR_WARNING"; 55 | case CommandID::SET_TIME: 56 | return "SET_TIME"; 57 | case CommandID::TEST_COUNTER: 58 | return "TEST_COUNTER"; 59 | case CommandID::TEST_TIME: 60 | return "TEST_TIME"; 61 | case CommandID::USER_AUTHENTICATE: 62 | return "USER_AUTHENTICATE"; 63 | case CommandID::GET_USER_PASSWORD_RETRY_COUNT: 64 | return "GET_USER_PASSWORD_RETRY_COUNT"; 65 | case CommandID::USER_AUTHORIZE: 66 | return "USER_AUTHORIZE"; 67 | case CommandID::UNLOCK_USER_PASSWORD: 68 | return "UNLOCK_USER_PASSWORD"; 69 | case CommandID::LOCK_DEVICE: 70 | return "LOCK_DEVICE"; 71 | case CommandID::FACTORY_RESET: 72 | return "FACTORY_RESET"; 73 | case CommandID::CHANGE_USER_PIN: 74 | return "CHANGE_USER_PIN"; 75 | case CommandID::CHANGE_ADMIN_PIN: 76 | return "CHANGE_ADMIN_PIN"; 77 | case CommandID::FIRMWARE_UPDATE: 78 | return "FIRMWARE_UPDATE"; 79 | case CommandID::FIRMWARE_PASSWORD_CHANGE: 80 | return "FIRMWARE_PASSWORD_CHANGE"; 81 | 82 | case CommandID::ENABLE_CRYPTED_PARI: 83 | return "ENABLE_CRYPTED_PARI"; 84 | case CommandID::DISABLE_CRYPTED_PARI: 85 | return "DISABLE_CRYPTED_PARI"; 86 | case CommandID::ENABLE_HIDDEN_CRYPTED_PARI: 87 | return "ENABLE_HIDDEN_CRYPTED_PARI"; 88 | case CommandID::DISABLE_HIDDEN_CRYPTED_PARI: 89 | return "DISABLE_HIDDEN_CRYPTED_PARI"; 90 | case CommandID::ENABLE_FIRMWARE_UPDATE: 91 | return "ENABLE_FIRMWARE_UPDATE"; 92 | case CommandID::EXPORT_FIRMWARE_TO_FILE: 93 | return "EXPORT_FIRMWARE_TO_FILE"; 94 | case CommandID::GENERATE_NEW_KEYS: 95 | return "GENERATE_NEW_KEYS"; 96 | case CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS: 97 | return "FILL_SD_CARD_WITH_RANDOM_CHARS"; 98 | 99 | case CommandID::WRITE_STATUS_DATA: 100 | return "WRITE_STATUS_DATA"; 101 | case CommandID::ENABLE_READONLY_UNCRYPTED_LUN: 102 | return "ENABLE_READONLY_UNCRYPTED_LUN"; 103 | case CommandID::ENABLE_READWRITE_UNCRYPTED_LUN: 104 | return "ENABLE_READWRITE_UNCRYPTED_LUN"; 105 | 106 | case CommandID::SEND_PASSWORD_MATRIX: 107 | return "SEND_PASSWORD_MATRIX"; 108 | case CommandID::SEND_PASSWORD_MATRIX_PINDATA: 109 | return "SEND_PASSWORD_MATRIX_PINDATA"; 110 | case CommandID::SEND_PASSWORD_MATRIX_SETUP: 111 | return "SEND_PASSWORD_MATRIX_SETUP"; 112 | 113 | case CommandID::GET_DEVICE_STATUS: 114 | return "GET_DEVICE_STATUS"; 115 | case CommandID::SEND_DEVICE_STATUS: 116 | return "SEND_DEVICE_STATUS"; 117 | 118 | case CommandID::SEND_HIDDEN_VOLUME_PASSWORD: 119 | return "SEND_HIDDEN_VOLUME_PASSWORD"; 120 | case CommandID::SEND_HIDDEN_VOLUME_SETUP: 121 | return "SEND_HIDDEN_VOLUME_SETUP"; 122 | case CommandID::SEND_PASSWORD: 123 | return "SEND_PASSWORD"; 124 | case CommandID::SEND_NEW_PASSWORD: 125 | return "SEND_NEW_PASSWORD"; 126 | case CommandID::CLEAR_NEW_SD_CARD_FOUND: 127 | return "CLEAR_NEW_SD_CARD_FOUND"; 128 | 129 | case CommandID::SEND_STARTUP: 130 | return "SEND_STARTUP"; 131 | case CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED: 132 | return "SEND_CLEAR_STICK_KEYS_NOT_INITIATED"; 133 | case CommandID::SEND_LOCK_STICK_HARDWARE: 134 | return "SEND_LOCK_STICK_HARDWARE"; 135 | 136 | case CommandID::PRODUCTION_TEST: 137 | return "PRODUCTION_TEST"; 138 | case CommandID::SEND_DEBUG_DATA: 139 | return "SEND_DEBUG_DATA"; 140 | 141 | case CommandID::CHANGE_UPDATE_PIN: 142 | return "CHANGE_UPDATE_PIN"; 143 | 144 | case CommandID::ENABLE_ADMIN_READONLY_UNCRYPTED_LUN: 145 | return "ENABLE_ADMIN_READONLY_UNCRYPTED_LUN"; 146 | case CommandID::ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN: 147 | return "ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN"; 148 | case CommandID::ENABLE_ADMIN_READONLY_ENCRYPTED_LUN: 149 | return "ENABLE_ADMIN_READONLY_ENCRYPTED_LUN"; 150 | case CommandID::ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN: 151 | return "ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN"; 152 | case CommandID::CHECK_SMARTCARD_USAGE: 153 | return "CHECK_SMARTCARD_USAGE"; 154 | 155 | case CommandID::GET_PW_SAFE_SLOT_STATUS: 156 | return "GET_PW_SAFE_SLOT_STATUS"; 157 | case CommandID::GET_PW_SAFE_SLOT_NAME: 158 | return "GET_PW_SAFE_SLOT_NAME"; 159 | case CommandID::GET_PW_SAFE_SLOT_PASSWORD: 160 | return "GET_PW_SAFE_SLOT_PASSWORD"; 161 | case CommandID::GET_PW_SAFE_SLOT_LOGINNAME: 162 | return "GET_PW_SAFE_SLOT_LOGINNAME"; 163 | case CommandID::SET_PW_SAFE_SLOT_DATA_1: 164 | return "SET_PW_SAFE_SLOT_DATA_1"; 165 | case CommandID::SET_PW_SAFE_SLOT_DATA_2: 166 | return "SET_PW_SAFE_SLOT_DATA_2"; 167 | case CommandID::PW_SAFE_ERASE_SLOT: 168 | return "PW_SAFE_ERASE_SLOT"; 169 | case CommandID::PW_SAFE_ENABLE: 170 | return "PW_SAFE_ENABLE"; 171 | case CommandID::PW_SAFE_INIT_KEY: 172 | return "PW_SAFE_INIT_KEY"; 173 | case CommandID::PW_SAFE_SEND_DATA: 174 | return "PW_SAFE_SEND_DATA"; 175 | case CommandID::SD_CARD_HIGH_WATERMARK: 176 | return "SD_CARD_HIGH_WATERMARK"; 177 | case CommandID::DETECT_SC_AES: 178 | return "DETECT_SC_AES"; 179 | case CommandID::NEW_AES_KEY: 180 | return "NEW_AES_KEY"; 181 | case CommandID::WRITE_TO_SLOT_2: 182 | return "WRITE_TO_SLOT_2"; 183 | case CommandID::SEND_OTP_DATA: 184 | return "SEND_OTP_DATA"; 185 | case CommandID::WINK: 186 | return "WINK"; 187 | case CommandID::GET_RANDOM: 188 | return "GET_RANDOM"; 189 | } 190 | return "UNKNOWN"; 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /data/41-nitrokey.rules: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2022 Nitrokey GmbH 3 | # 4 | # This file is part of libnitrokey. 5 | # 6 | # libnitrokey is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Lesser General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # any later version. 10 | # 11 | # libnitrokey is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public License 17 | # along with libnitrokey. If not, see . 18 | # 19 | # SPDX-License-Identifier: LGPL-3.0 20 | # 21 | 22 | # Here rules in new style should be provided. Matching devices should be tagged with 'uaccess'. 23 | # File prefix number should be lower than 73, to be correctly processed by the Udev. 24 | # Recommended udev version: >= 188. 25 | # 26 | ACTION!="add|change", GOTO="u2f_end" 27 | 28 | # Nitrokey U2F 29 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess" 30 | # Nitrokey FIDO U2F 31 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess" 32 | # Nitrokey FIDO2 33 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", TAG+="uaccess" 34 | # Nitrokey 3A Mini/3A NFC/3C NFC 35 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b2", TAG+="uaccess" 36 | # Nitrokey 3A NFC Bootloader/3C NFC Bootloader 37 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42dd", TAG+="uaccess" 38 | # Nitrokey 3A Mini Bootloader 39 | ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42e8", TAG+="uaccess" 40 | 41 | LABEL="u2f_end" 42 | 43 | 44 | SUBSYSTEM!="usb", GOTO="gnupg_rules_end" 45 | ACTION!="add", GOTO="gnupg_rules_end" 46 | 47 | # USB SmartCard Readers 48 | ## Crypto Stick 1.2 49 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4107", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" 50 | ## Nitrokey Pro 51 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4108", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" 52 | ## Nitrokey Pro Bootloader 53 | ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b4", TAG+="uaccess" 54 | ## Nitrokey Storage 55 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4109", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" 56 | ## Nitrokey Storage Bootloader 57 | ATTR{idVendor}=="03eb", ATTR{idProduct}=="2ff1", TAG+="uaccess" 58 | ## Nitrokey Start 59 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4211", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" 60 | ## Nitrokey HSM 61 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4230", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" 62 | 63 | LABEL="gnupg_rules_end" 64 | 65 | 66 | # Nitrokey Storage dev Entry 67 | KERNEL=="sd?1", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4109", SYMLINK+="nitrospace" 68 | -------------------------------------------------------------------------------- /data/41-nitrokey_old.rules: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015-2022 Nitrokey GmbH 3 | # 4 | # This file is part of libnitrokey. 5 | # 6 | # libnitrokey is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Lesser General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # any later version. 10 | # 11 | # libnitrokey is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public License 17 | # along with libnitrokey. If not, see . 18 | # 19 | # SPDX-License-Identifier: LGPL-3.0 20 | # 21 | 22 | # Here rules in old style should be provided. Matching devices should be added to 'plugdev' group, 23 | # and with mode set to "0660". 24 | # File prefix number should be lower than 73, to be correctly processed by the Udev. 25 | # Recommended udev version: < 188. 26 | # 27 | ACTION!="add|change", GOTO="u2f_end" 28 | 29 | # Nitrokey U2F 30 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", MODE="0660", GROUP+="plugdev" 31 | # Nitrokey FIDO U2F 32 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", MODE="0660", GROUP+="plugdev" 33 | # Nitrokey FIDO2 34 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", MODE="0660", GROUP+="plugdev" 35 | # Nitrokey 3A Mini/3A NFC/3C NFC 36 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b2", MODE="0660", GROUP+="plugdev" 37 | # Nitrokey 3A NFC Bootloader/3C NFC Bootloader 38 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42dd", MODE="0660", GROUP+="plugdev" 39 | # Nitrokey 3A Mini Bootloader 40 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42e8", MODE="0660", GROUP+="plugdev" 41 | 42 | LABEL="u2f_end" 43 | 44 | 45 | SUBSYSTEM!="usb", GOTO="gnupg_rules_end" 46 | ACTION!="add", GOTO="gnupg_rules_end" 47 | 48 | # USB SmartCard Readers 49 | ## Crypto Stick 1.2 50 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4107", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", MODE="0660", GROUP+="plugdev" 51 | ## Nitrokey Pro 52 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4108", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", MODE="0660", GROUP+="plugdev" 53 | ## Nitrokey Pro Bootloader 54 | ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b4", MODE="0660", GROUP+="plugdev" 55 | ## Nitrokey Storage 56 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4109", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", MODE="0660", GROUP+="plugdev" 57 | ## Nitrokey Storage Bootloader 58 | ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff1", MODE="0660", GROUP+="plugdev" 59 | ## Nitrokey Start 60 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4211", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", MODE="0660", GROUP+="plugdev" 61 | ## Nitrokey HSM 62 | ATTR{idVendor}=="20a0", ATTR{idProduct}=="4230", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", MODE="0660", GROUP+="plugdev" 63 | 64 | LABEL="gnupg_rules_end" 65 | 66 | 67 | # Nitrokey Storage dev Entry 68 | KERNEL=="sd?1", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4109", SYMLINK+="nitrospace" 69 | -------------------------------------------------------------------------------- /data/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | click = "*" 10 | regex = "*" 11 | 12 | [requires] 13 | python_version = "3.8" 14 | 15 | 16 | -------------------------------------------------------------------------------- /data/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "dbef89dbb42c3ae6b022f78b5e003cca039534820aa0e047b11872492211927b" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "click": { 20 | "hashes": [ 21 | "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", 22 | "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" 23 | ], 24 | "index": "pypi", 25 | "version": "==8.1.3" 26 | }, 27 | "regex": { 28 | "hashes": [ 29 | "sha256:02543d6d5c32d361b7cc468079ba4cddaaf4a6544f655901ba1ff9d8e3f18755", 30 | "sha256:036d1c1fbe69eba3ee253c107e71749cdbb4776db93d674bc0d5e28f30300734", 31 | "sha256:071bcb625e890f28b7c4573124a6512ea65107152b1d3ca101ce33a52dad4593", 32 | "sha256:0f8da3145f4b72f7ce6181c804eaa44cdcea313c8998cdade3d9e20a8717a9cb", 33 | "sha256:0fb6cb16518ac7eff29d1e0b0cce90275dfae0f17154165491058c31d58bdd1d", 34 | "sha256:0fd464e547dbabf4652ca5fe9d88d75ec30182981e737c07b3410235a44b9939", 35 | "sha256:12af15b6edb00e425f713160cfd361126e624ec0de86e74f7cad4b97b7f169b3", 36 | "sha256:165cc75cfa5aa0f12adb2ac6286330e7229a06dc0e6c004ec35da682b5b89579", 37 | "sha256:1a07e8366115069f26822c47732122ab61598830a69f5629a37ea8881487c107", 38 | "sha256:1c2de7f32fa87d04d40f54bce3843af430697aba51c3a114aa62837a0772f219", 39 | "sha256:253f858a0255cd91a0424a4b15c2eedb12f20274f85731b0d861c8137e843065", 40 | "sha256:275afc7352982ee947fc88f67a034b52c78395977b5fc7c9be15f7dc95b76f06", 41 | "sha256:2bde99f2cdfd6db1ec7e02d68cadd384ffe7413831373ea7cc68c5415a0cb577", 42 | "sha256:3241db067a7f69da57fba8bca543ac8a7ca415d91e77315690202749b9fdaba1", 43 | "sha256:37903d5ca11fa47577e8952d2e2c6de28553b11c70defee827afb941ab2c6729", 44 | "sha256:3dfbadb7b74d95f72f9f9dbf9778f7de92722ab520a109ceaf7927461fa85b10", 45 | "sha256:3e35c50b27f36176c792738cb9b858523053bc495044d2c2b44db24376b266f1", 46 | "sha256:3e9e983fc8e0d4d5ded7caa5aed39ca2cf6026d7e39801ef6f0af0b1b6cd9276", 47 | "sha256:3f6bd8178cce5bb56336722d5569d19c50bba5915a69a2050c497fb921e7cb0f", 48 | "sha256:43ee0df35925ae4b0cc6ee3f60b73369e559dd2ac40945044da9394dd9d3a51d", 49 | "sha256:45b761406777a681db0c24686178532134c937d24448d9e085279b69e9eb7da4", 50 | "sha256:46cbc5b23f85e94161b093dba1b49035697cf44c7db3c930adabfc0e6d861b95", 51 | "sha256:4f2e2cef324ca9355049ee1e712f68e2e92716eba24275e6767b9bfa15f1f478", 52 | "sha256:50b77622016f03989cd06ecf6b602c7a6b4ed2e3ce04133876b041d109c934ee", 53 | "sha256:582ea06079a03750b5f71e20a87cd99e646d796638b5894ff85987ebf5e04924", 54 | "sha256:58521abdab76583bd41ef47e5e2ddd93b32501aee4ee8cee71dee10a45ba46b1", 55 | "sha256:5b9c7b6895a01204296e9523b3e12b43e013835a9de035a783907c2c1bc447f0", 56 | "sha256:6165e737acb3bea3271372e8aa5ebe7226c8a8e8da1b94af2d6547c5a09d689d", 57 | "sha256:66fb765b2173d90389384708e3e1d3e4be1148bd8d4d50476b1469da5a2f0229", 58 | "sha256:68aed3fb0c61296bd6d234f558f78c51671f79ccb069cbcd428c2eea6fee7a5b", 59 | "sha256:6a0ef57cccd8089b4249eebad95065390e56c04d4a92c51316eab4131bca96a9", 60 | "sha256:709396c0c95b95045fac89b94f997410ff39b81a09863fe21002f390d48cc7d3", 61 | "sha256:73ed1b06abadbf6b61f6033a07c06f36ec0ddca117e41ef2ac37056705e46458", 62 | "sha256:7a608022f4593fc67518c6c599ae5abdb03bb8acd75993c82cd7a4c8100eff81", 63 | "sha256:7c4d9770e579eb11b582b2e2fd19fa204a15cb1589ae73cd4dcbb63b64f3e828", 64 | "sha256:7dbc96419ef0fb6ac56626014e6d3a345aeb8b17a3df8830235a88626ffc8d84", 65 | "sha256:7f271d0831d8ebc56e17b37f9fa1824b0379221d1238ae77c18a6e8c47f1fdce", 66 | "sha256:82b7fc67e49fdce671bdbec1127189fc979badf062ce6e79dc95ef5e07a8bf92", 67 | "sha256:85b7ee4d0c7a46296d884f6b489af8b960c4291d76aea4b22fd4fbe05e6ec08e", 68 | "sha256:8b747cef8e5dcdaf394192d43a0c02f5825aeb0ecd3d43e63ae500332ab830b0", 69 | "sha256:8bf867ba71856414a482e4b683500f946c300c4896e472e51d3db8dfa8dc8f32", 70 | "sha256:8e0da7ef160d4f3eb3d4d3e39a02c3c42f7dbcfce62c81f784cc99fc7059765f", 71 | "sha256:8e7d33f93cdd01868327d834d0f5bb029241cd293b47d51b96814dec27fc9b4b", 72 | "sha256:92183e9180c392371079262879c6532ccf55f808e6900df5d9f03c9ca8807255", 73 | "sha256:92ad03f928675ca05b79d3b1d3dfc149e2226d57ed9d57808f82105d511d0212", 74 | "sha256:97af238389cb029d63d5f2d931a7e8f5954ad96e812de5faaed373b68e74df86", 75 | "sha256:9913bcf730eb6e9b441fb176832eea9acbebab6035542c7c89d90c803f5cd3be", 76 | "sha256:9dae5affbb66178dad6c6fd5b02221ca9917e016c75ee3945e9a9563eb1fbb6f", 77 | "sha256:a850f5f369f1e3b6239da7fb43d1d029c1e178263df671819889c47caf7e4ff3", 78 | "sha256:aa6daa189db9104787ff1fd7a7623ce017077aa59eaac609d0d25ba95ed251a0", 79 | "sha256:aabc28f7599f781ddaeac168d0b566d0db82182cc3dcf62129f0a4fc2927b811", 80 | "sha256:af1e687ffab18a75409e5e5d6215b6ccd41a5a1a0ea6ce9665e01253f737a0d3", 81 | "sha256:b1d53835922cd0f9b74b2742453a444865a70abae38d12eb41c59271da66f38d", 82 | "sha256:b2df3ede85d778c949d9bd2a50237072cee3df0a423c91f5514f78f8035bde87", 83 | "sha256:b415b82e5be7389ec5ee7ee35431e4a549ea327caacf73b697c6b3538cb5c87f", 84 | "sha256:b7ba3c304a4a5d8112dbd30df8b3e4ef59b4b07807957d3c410d9713abaee9a8", 85 | "sha256:bcc6f7a3a95119c3568c572ca167ada75f8319890706283b9ba59b3489c9bcb3", 86 | "sha256:be392d9cd5309509175a9d7660dc17bf57084501108dbff0c5a8bfc3646048c3", 87 | "sha256:bea61de0c688198e3d9479344228c7accaa22a78b58ec408e41750ebafee6c08", 88 | "sha256:bedb3d01ad35ea1745bdb1d57f3ee0f996f988c98f5bbae9d068c3bb3065d210", 89 | "sha256:c36906a7855ec33a9083608e6cd595e4729dab18aeb9aad0dd0b039240266239", 90 | "sha256:c4fdf837666f7793a5c3cfa2f2f39f03eb6c7e92e831bc64486c2f547580c2b3", 91 | "sha256:cfad3a770839aa456ff9a9aa0e253d98b628d005a3ccb37da1ff9be7c84fee16", 92 | "sha256:d128e278e5e554c5c022c7bed410ca851e00bacebbb4460de546a73bc53f8de4", 93 | "sha256:dffd9114ade73137ab2b79a8faf864683dbd2dbbb6b23a305fbbd4cbaeeb2187", 94 | "sha256:e2acf5c66fbb62b5fe4c40978ddebafa50818f00bf79d60569d9762f6356336e", 95 | "sha256:e65580ae3137bce712f505ec7c2d700aef0014a3878c4767b74aff5895fc454f", 96 | "sha256:e944268445b5694f5d41292c9228f0ca46d5a32a67f195d5f8547c1f1d91f4bc", 97 | "sha256:ed26c3d2d62c6588e0dad175b8d8cc0942a638f32d07b80f92043e5d73b7db67", 98 | "sha256:ed625205f5f26984382b68e4cbcbc08e6603c9e84c14b38457170b0cc71c823b", 99 | "sha256:f2a5d9f612091812dee18375a45d046526452142e7b78c4e21ab192db15453d5", 100 | "sha256:f86aef546add4ff1202e1f31e9bb54f9268f17d996b2428877283146bf9bc013", 101 | "sha256:f89d26e50a4c7453cb8c415acd09e72fbade2610606a9c500a1e48c43210a42d", 102 | "sha256:fb7107faf0168de087f62a2f2ed00f9e9da12e0b801582b516ddac236b871cda" 103 | ], 104 | "index": "pypi", 105 | "version": "==2022.4.24" 106 | } 107 | }, 108 | "develop": {} 109 | } 110 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | 2 | The specified USB IDs in the Udev rules file are as follows: 3 | 4 | | Name | USB ID | 5 | |----------------------------------------------|:---------:| 6 | | Crypto Stick 1.2 | 20a0:4107 | 7 | | Nitrokey 3A Mini/3A NFC/3C NFC | 20a0:42b2 | 8 | | Nitrokey 3A NFC Bootloader/3C NFC Bootloader | 20a0:42dd | 9 | | Nitrokey 3A Mini Bootloader | 20a0:42e8 | 10 | | Nitrokey FIDO U2F | 20a0:4287 | 11 | | Nitrokey FIDO2 | 20a0:42b1 | 12 | | Nitrokey HSM | 20a0:4230 | 13 | | Nitrokey Pro | 20a0:4108 | 14 | | Nitrokey Pro Bootloader | 20a0:42b4 | 15 | | Nitrokey Start | 20a0:4211 | 16 | | Nitrokey Storage | 20a0:4109 | 17 | | Nitrokey Storage Bootloader | 03eb:2ff1 | 18 | | Nitrokey U2F | 2581:f1d0 | 19 | 20 | Generated from `41-nitrokey.rules`. -------------------------------------------------------------------------------- /data/generate_udev_docs.py: -------------------------------------------------------------------------------- 1 | import click 2 | import regex as re 3 | 4 | 5 | @click.command() 6 | @click.argument('input', type=click.File('rt')) 7 | def main(input: click.File): 8 | print(f"| {'Name':40s} | {'USB ID':9s} |") 9 | print("|------------------------------------------|:---------:|") 10 | name = '' 11 | res = [] 12 | for l in input: 13 | l = l.strip() 14 | if l.startswith("#"): 15 | name = l.strip("#").strip() 16 | continue 17 | pid = re.findall(r'ATTR[S]?\{idProduct\}=="(\w+)"', l, re.VERSION1) 18 | vid = re.findall(r'ATTR[S]?\{idVendor\}=="(\w+)"', l, re.VERSION1) 19 | if pid: 20 | res.append((name, vid[0], pid[0])) 21 | 22 | res = sorted(res) 23 | for r in res: 24 | if "dev Entry" in r[0]: 25 | continue 26 | print(f"| {r[0]:40s} | {r[1]}:{r[2]} |") 27 | 28 | print() 29 | print(f'Generated from {input.name}') 30 | 31 | 32 | if __name__ == '__main__': 33 | main() 34 | -------------------------------------------------------------------------------- /libnitrokey.pc.in: -------------------------------------------------------------------------------- 1 | libdir=@CMAKE_INSTALL_FULL_LIBDIR@ 2 | includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ 3 | 4 | Name: libnitrokey 5 | Description: Library for communicating with Nitrokey in a clean and easy manner 6 | Version: @libnitrokey_VERSION@ 7 | Requires.private: @HIDAPI_LIBUSB_NAME@ 8 | 9 | Libs: -L${libdir} -lnitrokey 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /libnitrokey.pro: -------------------------------------------------------------------------------- 1 | # Created by and for Qt Creator. This file was created for editing the project sources only. 2 | # You may attempt to use it for building too, by modifying this file here. 3 | 4 | CONFIG += c++14 shared debug 5 | 6 | 7 | TEMPLATE = lib 8 | TARGET = nitrokey 9 | 10 | VERSION = 3.7.0 11 | LIBNK_VERSION_MAJOR = 3 12 | LIBNK_VERSION_MINOR = 7 13 | 14 | QMAKE_TARGET_COMPANY = Nitrokey 15 | QMAKE_TARGET_PRODUCT = libnitrokey 16 | QMAKE_TARGET_DESCRIPTION = Communicate with Nitrokey stick devices in a clean and easy manner 17 | QMAKE_TARGET_COPYRIGHT = Copyright (c) 2015-2022 Nitrokey Gmbh 18 | 19 | HEADERS = \ 20 | $$PWD/hidapi/hidapi/hidapi.h \ 21 | $$PWD/libnitrokey/command.h \ 22 | $$PWD/libnitrokey/command_id.h \ 23 | $$PWD/libnitrokey/CommandFailedException.h \ 24 | $$PWD/libnitrokey/cxx_semantics.h \ 25 | $$PWD/libnitrokey/device.h \ 26 | $$PWD/libnitrokey/device_proto.h \ 27 | $$PWD/libnitrokey/DeviceCommunicationExceptions.h \ 28 | $$PWD/libnitrokey/dissect.h \ 29 | $$PWD/libnitrokey/LibraryException.h \ 30 | $$PWD/libnitrokey/log.h \ 31 | $$PWD/libnitrokey/version.h \ 32 | $$PWD/libnitrokey/LongOperationInProgressException.h \ 33 | $$PWD/libnitrokey/misc.h \ 34 | $$PWD/libnitrokey/NitrokeyManager.h \ 35 | $$PWD/libnitrokey/stick10_commands.h \ 36 | $$PWD/libnitrokey/stick10_commands_0.8.h \ 37 | $$PWD/libnitrokey/stick20_commands.h \ 38 | $$PWD/NK_C_API.h 39 | 40 | 41 | SOURCES = \ 42 | $$PWD/command_id.cc \ 43 | $$PWD/device.cc \ 44 | $$PWD/DeviceCommunicationExceptions.cpp \ 45 | $$PWD/log.cc \ 46 | $$PWD/version.cc \ 47 | $$PWD/misc.cc \ 48 | $$PWD/NitrokeyManager.cc \ 49 | $$PWD/NK_C_API.cc 50 | 51 | 52 | tests { 53 | SOURCES += \ 54 | $$PWD/unittest/catch_main.cpp \ 55 | $$PWD/unittest/test.cc \ 56 | $$PWD/unittest/test2.cc \ 57 | $$PWD/unittest/test3.cc \ 58 | $$PWD/unittest/test_C_API.cpp \ 59 | $$PWD/unittest/test_HOTP.cc 60 | } 61 | 62 | unix:!macx{ 63 | # SOURCES += $$PWD/hidapi/linux/hid.c 64 | LIBS += -lhidapi-libusb 65 | } 66 | 67 | macx{ 68 | SOURCES += $$PWD/hidapi/mac/hid.c 69 | LIBS+= -framework IOKit -framework CoreFoundation 70 | } 71 | 72 | win32 { 73 | SOURCES += $$PWD/hidapi/windows/hid.c 74 | LIBS += -lsetupapi 75 | } 76 | 77 | INCLUDEPATH = \ 78 | $$PWD/. \ 79 | $$PWD/hidapi/hidapi \ 80 | $$PWD/libnitrokey \ 81 | $$PWD/libnitrokey/hidapi \ 82 | $$PWD/unittest \ 83 | $$PWD/unittest/Catch/single_include 84 | 85 | unix:!macx{ 86 | # Install rules for QMake (CMake is preffered though) 87 | udevrules.path = $$system(pkg-config --variable=udevdir udev) 88 | isEmpty(udevrules.path){ 89 | udevrules.path = "/lib/udev/" 90 | message("Could not detect path for udev rules - setting default: " $$udevrules.path) 91 | } 92 | udevrules.path = $$udevrules.path"/rules.d" 93 | udevrules.files = $$PWD/"data/41-nitrokey.rules" 94 | message ($$udevrules.files) 95 | INSTALLS +=udevrules 96 | 97 | headers.files = $$HEADERS 98 | headers.path = /usr/local/include/libnitrokey/ 99 | INSTALLS += headers 100 | 101 | libbin.path = /usr/local/lib 102 | INSTALLS += libbin 103 | } 104 | 105 | DEFINES += LIBNK_GIT_VERSION=\"\\\"$$system(git describe --abbrev=4 --always)\\\"\" LIBNK_VERSION="\\\"$${VERSION}\\\"" 106 | DEFINES += LIBNK_VERSION_MAJOR=$${LIBNK_VERSION_MAJOR} LIBNK_VERSION_MINOR=$${LIBNK_VERSION_MINOR} 107 | message($$DEFINES) 108 | -------------------------------------------------------------------------------- /libnitrokey/CommandFailedException.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef LIBNITROKEY_COMMANDFAILEDEXCEPTION_H 23 | #define LIBNITROKEY_COMMANDFAILEDEXCEPTION_H 24 | 25 | #include 26 | #include 27 | #include "log.h" 28 | #include "command_id.h" 29 | 30 | using cs = nitrokey::proto::stick10::command_status; 31 | using cs2 = nitrokey::proto::stick20::device_status; 32 | 33 | class CommandFailedException : public std::exception { 34 | public: 35 | const uint8_t last_command_id; 36 | const uint8_t last_command_status; 37 | 38 | CommandFailedException(uint8_t last_command_id_, uint8_t last_command_status_) : 39 | last_command_id(last_command_id_), 40 | last_command_status(last_command_status_) { 41 | LOG(std::string("CommandFailedException, status: ")+ std::to_string(last_command_status), nitrokey::log::Loglevel::DEBUG); 42 | } 43 | 44 | virtual const char *what() const noexcept override { 45 | return "Command execution has failed on device"; 46 | } 47 | 48 | 49 | bool reason_timestamp_warning() const noexcept { 50 | return last_command_status == static_cast(cs::timestamp_warning); 51 | } 52 | 53 | bool reason_AES_not_initialized() const noexcept { 54 | return last_command_status == static_cast(cs::AES_dec_failed); 55 | } 56 | 57 | bool reason_not_authorized() const noexcept { 58 | return last_command_status == static_cast(cs::not_authorized); 59 | } 60 | 61 | bool reason_slot_not_programmed() const noexcept { 62 | return last_command_status == static_cast(cs::slot_not_programmed); 63 | } 64 | 65 | bool reason_wrong_password() const noexcept { 66 | return last_command_status == static_cast(cs::wrong_password); 67 | } 68 | 69 | bool reason_smartcard_busy() const noexcept { 70 | return last_command_status == static_cast(cs2::smartcard_error); 71 | } 72 | 73 | }; 74 | 75 | 76 | #endif //LIBNITROKEY_COMMANDFAILEDEXCEPTION_H 77 | -------------------------------------------------------------------------------- /libnitrokey/DeviceCommunicationExceptions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | 23 | #ifndef LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H 24 | #define LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | class DeviceCommunicationException: public std::runtime_error 33 | { 34 | std::string message; 35 | static std::atomic_int occurred; 36 | public: 37 | explicit DeviceCommunicationException(const std::string& _msg): std::runtime_error(_msg), message(_msg){ 38 | ++occurred; 39 | } 40 | uint8_t getType() const {return 1;} 41 | // virtual const char* what() const noexcept override { 42 | // return message.c_str(); 43 | // } 44 | static bool has_occurred(){ return occurred > 0; } 45 | static void reset_occurred_flag(){ occurred = 0; } 46 | }; 47 | 48 | class DeviceNotConnected: public DeviceCommunicationException { 49 | public: 50 | DeviceNotConnected(std::string msg) : DeviceCommunicationException(msg){} 51 | uint8_t getType() const {return 2;} 52 | }; 53 | 54 | class DeviceSendingFailure: public DeviceCommunicationException { 55 | public: 56 | DeviceSendingFailure(std::string msg) : DeviceCommunicationException(msg){} 57 | uint8_t getType() const {return 3;} 58 | }; 59 | 60 | class DeviceReceivingFailure: public DeviceCommunicationException { 61 | public: 62 | DeviceReceivingFailure(std::string msg) : DeviceCommunicationException(msg){} 63 | uint8_t getType() const {return 4;} 64 | }; 65 | 66 | class InvalidCRCReceived: public DeviceReceivingFailure { 67 | public: 68 | InvalidCRCReceived(std::string msg) : DeviceReceivingFailure(msg){} 69 | uint8_t getType() const {return 5;} 70 | }; 71 | 72 | 73 | #endif //LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H 74 | -------------------------------------------------------------------------------- /libnitrokey/LibraryException.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef LIBNITROKEY_LIBRARYEXCEPTION_H 23 | #define LIBNITROKEY_LIBRARYEXCEPTION_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "log.h" 30 | 31 | class LibraryException: std::exception { 32 | public: 33 | virtual uint8_t exception_id()= 0; 34 | }; 35 | 36 | // Use static string object for keeping the c_str message for the caller. 37 | // Strings collection used as an alternative to memory leaks done via strdup(). 38 | // TargetBufferSmallerThanSource Exception should never happen in a correctly written library client. 39 | static std::vector g_exception_messages; 40 | static std::mutex g_exception_message_mutex; 41 | 42 | class TargetBufferSmallerThanSource: public LibraryException { 43 | public: 44 | virtual uint8_t exception_id() override { 45 | return 203; 46 | } 47 | 48 | public: 49 | size_t source_size; 50 | size_t target_size; 51 | 52 | TargetBufferSmallerThanSource( 53 | size_t source_size_, size_t target_size_ 54 | ) : source_size(source_size_), target_size(target_size_) {} 55 | 56 | virtual const char *what() const noexcept override { 57 | std::lock_guard lock(g_exception_message_mutex); 58 | std::string s = " "; 59 | auto ts = [](size_t x){ return std::to_string(x); }; 60 | g_exception_messages.emplace_back(std::string("Target buffer size is smaller than source: [source size, buffer size]") 61 | + s + ts(source_size) + s + ts(target_size)); 62 | return g_exception_messages.back().c_str(); 63 | } 64 | 65 | }; 66 | 67 | class InvalidHexString : public LibraryException { 68 | public: 69 | virtual uint8_t exception_id() override { 70 | return 202; 71 | } 72 | 73 | public: 74 | uint8_t invalid_char; 75 | 76 | InvalidHexString (uint8_t invalid_char_) : invalid_char(invalid_char_) {} 77 | 78 | virtual const char *what() const noexcept override { 79 | return "Invalid character in hex string"; 80 | } 81 | 82 | }; 83 | 84 | class InvalidSlotException : public LibraryException { 85 | public: 86 | virtual uint8_t exception_id() override { 87 | return 201; 88 | } 89 | 90 | public: 91 | uint8_t slot_selected; 92 | 93 | InvalidSlotException(uint8_t slot_selected_) : slot_selected(slot_selected_) {} 94 | 95 | virtual const char *what() const noexcept override { 96 | return "Wrong slot selected"; 97 | } 98 | 99 | }; 100 | 101 | 102 | 103 | class TooLongStringException : public LibraryException { 104 | public: 105 | virtual uint8_t exception_id() override { 106 | return 200; 107 | } 108 | 109 | std::size_t size_source; 110 | std::size_t size_destination; 111 | std::string message; 112 | 113 | TooLongStringException(size_t size_source_, size_t size_destination_, const std::string &message_ = "") : size_source( 114 | size_source_), size_destination(size_destination_), message(message_) { 115 | LOG(std::string("TooLongStringException, size diff: ")+ std::to_string(size_source-size_destination), nitrokey::log::Loglevel::DEBUG); 116 | 117 | } 118 | 119 | virtual const char *what() const noexcept override { 120 | //TODO add sizes and message data to final message 121 | return "Too long string has been supplied as an argument"; 122 | } 123 | 124 | }; 125 | 126 | #endif //LIBNITROKEY_LIBRARYEXCEPTION_H 127 | -------------------------------------------------------------------------------- /libnitrokey/LongOperationInProgressException.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H 23 | #define LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H 24 | 25 | #include "CommandFailedException.h" 26 | 27 | class LongOperationInProgressException : public CommandFailedException { 28 | 29 | public: 30 | unsigned char progress_bar_value; 31 | 32 | LongOperationInProgressException( 33 | unsigned char command_id_, uint8_t last_command_status_, unsigned char progress_bar_value_) 34 | : CommandFailedException(command_id_, last_command_status_), progress_bar_value(progress_bar_value_){ 35 | LOG( 36 | std::string("LongOperationInProgressException, progress bar status: ")+ 37 | std::to_string(progress_bar_value), nitrokey::log::Loglevel::DEBUG); 38 | } 39 | virtual const char *what() const noexcept override { 40 | return "Device returned busy status with long operation in progress"; 41 | } 42 | }; 43 | 44 | 45 | #endif //LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H 46 | -------------------------------------------------------------------------------- /libnitrokey/command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef COMMAND_H 23 | #define COMMAND_H 24 | #include 25 | #include "command_id.h" 26 | #include "cxx_semantics.h" 27 | 28 | #define print_to_ss(x) ( ss << " " << (#x) <<":\t" << (x) << std::endl ); 29 | #define print_to_ss_int(x) ( ss << " " << (#x) <<":\t" << static_cast(x) << std::endl ); 30 | #ifdef LOG_VOLATILE_DATA 31 | #define print_to_ss_volatile(x) print_to_ss(x); 32 | #else 33 | #define print_to_ss_volatile(x) ( ss << " " << (#x) <<":\t" << "***********" << std::endl ); 34 | #endif 35 | #define hexdump_to_ss(x) (ss << #x":\n"\ 36 | << ::nitrokey::misc::hexdump(reinterpret_cast(&x), sizeof x, false)); 37 | 38 | namespace nitrokey { 39 | namespace proto { 40 | 41 | template 42 | class Command : semantics::non_constructible { 43 | public: 44 | constexpr static CommandID command_id() { return cmd_id; } 45 | 46 | template 47 | std::string dissect(const T &) { 48 | return std::string("Payload dissection is unavailable"); 49 | } 50 | }; 51 | 52 | namespace stick20{ 53 | enum class PasswordKind : uint8_t { 54 | User = 'P', 55 | Admin = 'A', 56 | AdminPrefixed 57 | }; 58 | 59 | template 60 | class PasswordCommand : public Command { 61 | constexpr static CommandID _command_id() { return cmd_id; } 62 | public: 63 | struct CommandPayload { 64 | uint8_t kind; 65 | uint8_t password[password_length]; 66 | 67 | std::string dissect() const { 68 | std::stringstream ss; 69 | print_to_ss( kind ); 70 | print_to_ss_volatile(password); 71 | return ss.str(); 72 | } 73 | void set_kind_admin() { 74 | kind = 'A'; 75 | } 76 | void set_kind_admin_prefixed() { 77 | kind = 'P'; 78 | } 79 | void set_kind_user() { 80 | kind = 'P'; 81 | } 82 | 83 | void set_defaults(){ 84 | set_kind(Tpassword_kind); 85 | } 86 | 87 | void set_kind(PasswordKind password_kind){ 88 | switch (password_kind){ 89 | case PasswordKind::Admin: 90 | set_kind_admin(); 91 | break; 92 | case PasswordKind::User: 93 | set_kind_user(); 94 | break; 95 | case PasswordKind::AdminPrefixed: 96 | set_kind_admin_prefixed(); 97 | break; 98 | } 99 | } 100 | 101 | } __packed; 102 | 103 | //typedef Transaction::command_id(), struct CommandPayload, struct EmptyPayload> 104 | // CommandTransaction; 105 | using CommandTransaction = Transaction; 106 | //using CommandTransaction = Transaction<_command_id(), CommandPayload, EmptyPayload>; 107 | 108 | }; 109 | } 110 | } 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /libnitrokey/command_id.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef COMMAND_ID_H 23 | #define COMMAND_ID_H 24 | #include 25 | 26 | namespace nitrokey { 27 | namespace proto { 28 | namespace stick20 { 29 | enum class device_status : uint8_t { 30 | idle = 0, 31 | ok, 32 | busy, 33 | wrong_password, 34 | busy_progressbar, 35 | password_matrix_ready, 36 | no_user_password_unlock, // FIXME: translate on receive to command status error (fix in firmware?) 37 | smartcard_error, 38 | security_bit_active 39 | }; 40 | const int CMD_START_VALUE = 0x20; 41 | const int CMD_END_VALUE = 0x60; 42 | } 43 | namespace stick10 { 44 | enum class command_status : uint8_t { 45 | ok = 0, 46 | wrong_CRC, 47 | wrong_slot, 48 | slot_not_programmed, 49 | wrong_password = 4, 50 | not_authorized, 51 | timestamp_warning, 52 | no_name_error, 53 | not_supported, 54 | unknown_command, 55 | AES_dec_failed 56 | }; 57 | enum class device_status : uint8_t { 58 | ok = 0, 59 | busy = 1, 60 | error, 61 | received_report, 62 | }; 63 | } 64 | 65 | 66 | enum class CommandID : uint8_t { 67 | GET_STATUS = 0x00, 68 | WRITE_TO_SLOT = 0x01, 69 | READ_SLOT_NAME = 0x02, 70 | READ_SLOT = 0x03, 71 | GET_CODE = 0x04, 72 | WRITE_CONFIG = 0x05, 73 | ERASE_SLOT = 0x06, 74 | FIRST_AUTHENTICATE = 0x07, 75 | AUTHORIZE = 0x08, 76 | GET_PASSWORD_RETRY_COUNT = 0x09, 77 | CLEAR_WARNING = 0x0A, 78 | SET_TIME = 0x0B, 79 | TEST_COUNTER = 0x0C, 80 | TEST_TIME = 0x0D, 81 | USER_AUTHENTICATE = 0x0E, 82 | GET_USER_PASSWORD_RETRY_COUNT = 0x0F, 83 | USER_AUTHORIZE = 0x10, 84 | UNLOCK_USER_PASSWORD = 0x11, 85 | LOCK_DEVICE = 0x12, 86 | FACTORY_RESET = 0x13, 87 | CHANGE_USER_PIN = 0x14, 88 | CHANGE_ADMIN_PIN = 0x15, 89 | WRITE_TO_SLOT_2 = 0x16, 90 | SEND_OTP_DATA = 0x17, 91 | FIRMWARE_UPDATE = 0x19, 92 | FIRMWARE_PASSWORD_CHANGE = 0x1A, 93 | GET_RANDOM = 0x1B, 94 | 95 | ENABLE_CRYPTED_PARI = 0x20, 96 | DISABLE_CRYPTED_PARI = 0x20 + 1, 97 | ENABLE_HIDDEN_CRYPTED_PARI = 0x20 + 2, 98 | DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3, 99 | ENABLE_FIRMWARE_UPDATE = 0x20 + 4, //enables update mode 100 | EXPORT_FIRMWARE_TO_FILE = 0x20 + 5, 101 | GENERATE_NEW_KEYS = 0x20 + 6, 102 | FILL_SD_CARD_WITH_RANDOM_CHARS = 0x20 + 7, 103 | 104 | WRITE_STATUS_DATA = 0x20 + 8, //@unused 105 | ENABLE_READONLY_UNCRYPTED_LUN = 0x20 + 9, 106 | ENABLE_READWRITE_UNCRYPTED_LUN = 0x20 + 10, 107 | 108 | SEND_PASSWORD_MATRIX = 0x20 + 11, //@unused 109 | SEND_PASSWORD_MATRIX_PINDATA = 0x20 + 12, //@unused 110 | SEND_PASSWORD_MATRIX_SETUP = 0x20 + 13, //@unused 111 | 112 | GET_DEVICE_STATUS = 0x20 + 14, 113 | SEND_DEVICE_STATUS = 0x20 + 15, 114 | 115 | SEND_HIDDEN_VOLUME_PASSWORD = 0x20 + 16, //@unused 116 | SEND_HIDDEN_VOLUME_SETUP = 0x20 + 17, 117 | SEND_PASSWORD = 0x20 + 18, 118 | SEND_NEW_PASSWORD = 0x20 + 19, 119 | CLEAR_NEW_SD_CARD_FOUND = 0x20 + 20, 120 | 121 | SEND_STARTUP = 0x20 + 21, 122 | SEND_CLEAR_STICK_KEYS_NOT_INITIATED = 0x20 + 22, 123 | SEND_LOCK_STICK_HARDWARE = 0x20 + 23, //locks firmware upgrade 124 | 125 | PRODUCTION_TEST = 0x20 + 24, 126 | SEND_DEBUG_DATA = 0x20 + 25, //@unused 127 | 128 | CHANGE_UPDATE_PIN = 0x20 + 26, 129 | 130 | //added in v0.48.5 131 | ENABLE_ADMIN_READONLY_UNCRYPTED_LUN = 0x20 + 28, 132 | ENABLE_ADMIN_READWRITE_UNCRYPTED_LUN = 0x20 + 29, 133 | ENABLE_ADMIN_READONLY_ENCRYPTED_LUN = 0x20 + 30, 134 | ENABLE_ADMIN_READWRITE_ENCRYPTED_LUN = 0x20 + 31, 135 | CHECK_SMARTCARD_USAGE = 0x20 + 32, 136 | //v0.52+ 137 | WINK = 0x20 + 33, 138 | 139 | GET_PW_SAFE_SLOT_STATUS = 0x60, 140 | GET_PW_SAFE_SLOT_NAME = 0x61, 141 | GET_PW_SAFE_SLOT_PASSWORD = 0x62, 142 | GET_PW_SAFE_SLOT_LOGINNAME = 0x63, 143 | SET_PW_SAFE_SLOT_DATA_1 = 0x64, 144 | SET_PW_SAFE_SLOT_DATA_2 = 0x65, 145 | PW_SAFE_ERASE_SLOT = 0x66, 146 | PW_SAFE_ENABLE = 0x67, 147 | PW_SAFE_INIT_KEY = 0x68, //@unused 148 | PW_SAFE_SEND_DATA = 0x69, //@unused 149 | SD_CARD_HIGH_WATERMARK = 0x70, 150 | DETECT_SC_AES = 0x6a, 151 | NEW_AES_KEY = 0x6b, 152 | }; 153 | 154 | const char *commandid_to_string(CommandID id); 155 | } 156 | } 157 | #endif 158 | -------------------------------------------------------------------------------- /libnitrokey/cxx_semantics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef CXX_SEMANTICS_H 23 | #define CXX_SEMANTICS_H 24 | 25 | #ifndef _MSC_VER 26 | #define __packed __attribute__((__packed__)) 27 | #else 28 | #define __packed 29 | #endif 30 | 31 | #ifdef _MSC_VER 32 | #define strdup _strdup 33 | #endif 34 | 35 | /* 36 | * There's no need to include Boost for a simple subset this project needs. 37 | */ 38 | namespace semantics { 39 | class non_constructible { 40 | non_constructible() {} 41 | }; 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /libnitrokey/deprecated.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | 23 | #ifndef LIBNITROKEY_DEPRECATED_H 24 | #define LIBNITROKEY_DEPRECATED_H 25 | 26 | #if defined(__GNUC__) || defined(__clang__) 27 | #define DEPRECATED __attribute__((deprecated)) 28 | #elif defined(_MSC_VER) 29 | #define DEPRECATED __declspec(deprecated) 30 | #else 31 | #pragma message("WARNING: DEPRECATED macro is not defined for this compiler") 32 | #define DEPRECATED 33 | #endif 34 | 35 | #endif //LIBNITROKEY_DEPRECATED_H 36 | -------------------------------------------------------------------------------- /libnitrokey/device.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef DEVICE_H 23 | #define DEVICE_H 24 | #include 25 | #include "hidapi/hidapi.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "misc.h" 32 | 33 | #define HID_REPORT_SIZE 65 34 | 35 | #include 36 | 37 | namespace nitrokey { 38 | namespace device { 39 | using namespace std::chrono_literals; 40 | using std::chrono::milliseconds; 41 | 42 | struct EnumClassHash 43 | { 44 | template 45 | std::size_t operator()(T t) const 46 | { 47 | return static_cast(t); 48 | } 49 | }; 50 | 51 | enum class DeviceModel{ 52 | PRO, 53 | STORAGE, 54 | LIBREM 55 | }; 56 | 57 | std::ostream& operator<<(std::ostream& stream, DeviceModel model); 58 | 59 | /** 60 | * The USB vendor ID for Nitrokey devices. 61 | */ 62 | extern const uint16_t NITROKEY_VID; 63 | /** 64 | * The USB product ID for the Nitrokey Pro. 65 | */ 66 | extern const uint16_t NITROKEY_PRO_PID; 67 | /** 68 | * The USB product ID for the Nitrokey Storage. 69 | */ 70 | extern const uint16_t NITROKEY_STORAGE_PID; 71 | /** 72 | * The USB vendor ID for Purism devices. 73 | */ 74 | extern const uint16_t PURISM_VID; 75 | /** 76 | * The USB product ID for the Librem Key. 77 | */ 78 | extern const uint16_t LIBREM_KEY_PID; 79 | 80 | /** 81 | * Convert the given USB product ID to a Nitrokey model. If there is no model 82 | * with that ID, return an absent value. 83 | */ 84 | misc::Option product_id_to_model(uint16_t product_id); 85 | misc::Option product_id_to_model(uint16_t vendor_id, uint16_t product_id); 86 | 87 | /** 88 | * Information about a connected device. 89 | * 90 | * This struct contains the information about a connected device returned by 91 | * hidapi when enumerating the connected devices. 92 | */ 93 | struct DeviceInfo { 94 | /** 95 | * The model of the connected device. 96 | */ 97 | DeviceModel m_deviceModel; 98 | /** 99 | * The USB connection path for the device. 100 | */ 101 | std::string m_path; 102 | /** 103 | * The serial number of the device. 104 | */ 105 | std::string m_serialNumber; 106 | }; 107 | 108 | #include 109 | 110 | class Device { 111 | 112 | public: 113 | 114 | struct ErrorCounters{ 115 | using cnt = std::atomic_int; 116 | cnt wrong_CRC; 117 | cnt CRC_other_than_awaited; 118 | cnt busy; 119 | cnt total_retries; 120 | cnt sending_error; 121 | cnt receiving_error; 122 | cnt total_comm_runs; 123 | cnt successful_storage_commands; 124 | cnt command_successful_recv; 125 | cnt recv_executed; 126 | cnt sends_executed; 127 | cnt busy_progressbar; 128 | cnt command_result_not_equal_0_recv; 129 | cnt communication_successful; 130 | cnt low_level_reconnect; 131 | std::string get_as_string(); 132 | 133 | } m_counters = {}; 134 | 135 | 136 | Device(const uint16_t vid, const uint16_t pid, const DeviceModel model, 137 | const milliseconds send_receive_delay, const int retry_receiving_count, 138 | const milliseconds retry_timeout); 139 | 140 | virtual ~Device(); 141 | 142 | // lack of device is not actually an error, 143 | // so it doesn't throw 144 | bool connect(); 145 | bool disconnect(); 146 | 147 | /* 148 | * Sends packet of HID_REPORT_SIZE. 149 | */ 150 | virtual int send(const void *packet); 151 | 152 | /* 153 | * Gets packet of HID_REPORT_SIZE. 154 | * Can sleep. See below. 155 | */ 156 | virtual int recv(void *packet); 157 | 158 | /*** 159 | * Returns true if some device is visible by OS with given VID and PID 160 | * whether the device is connected through HID API or not. 161 | * @return true if visible by OS 162 | */ 163 | bool could_be_enumerated(); 164 | /** 165 | * Returns a vector with all connected Nitrokey devices. 166 | * 167 | * @return information about all connected devices 168 | */ 169 | static std::vector enumerate(); 170 | 171 | /** 172 | * Create a Device of the given model. 173 | */ 174 | static std::shared_ptr create(DeviceModel model); 175 | 176 | 177 | void show_stats(); 178 | // ErrorCounters get_stats(){ return m_counters; } 179 | int get_retry_receiving_count() const { return m_retry_receiving_count; } 180 | int get_retry_sending_count() const { return m_retry_sending_count; } 181 | std::chrono::milliseconds get_retry_timeout() const { return m_retry_timeout; } 182 | std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;} 183 | 184 | int get_last_command_status() {int a = std::atomic_exchange(&last_command_status, static_cast(0)); return a;} 185 | void set_last_command_status(uint8_t _err) { last_command_status = _err;} 186 | bool last_command_sucessfull() const {return last_command_status == 0;} 187 | DeviceModel get_device_model() const {return m_model;} 188 | void set_receiving_delay(std::chrono::milliseconds delay); 189 | void set_retry_delay(std::chrono::milliseconds delay); 190 | static void set_default_device_speed(int delay); 191 | void setDefaultDelay(); 192 | void set_path(const std::string path); 193 | 194 | 195 | private: 196 | std::atomic last_command_status; 197 | void _reconnect(); 198 | bool _connect(); 199 | bool _disconnect(); 200 | 201 | protected: 202 | const uint16_t m_vid; 203 | const uint16_t m_pid; 204 | const DeviceModel m_model; 205 | 206 | /* 207 | * While the project uses Signal11 portable HIDAPI 208 | * library, there's no way of doing it asynchronously, 209 | * hence polling. 210 | */ 211 | const int m_retry_sending_count; 212 | const int m_retry_receiving_count; 213 | std::chrono::milliseconds m_retry_timeout; 214 | std::chrono::milliseconds m_send_receive_delay; 215 | std::atomicmp_devhandle; 216 | std::string m_path; 217 | 218 | static std::atomic_int instances_count; 219 | static std::chrono::milliseconds default_delay ; 220 | }; 221 | 222 | class Stick10 : public Device { 223 | public: 224 | Stick10(); 225 | 226 | }; 227 | 228 | class Stick20 : public Device { 229 | public: 230 | Stick20(); 231 | }; 232 | 233 | class LibremKey : public Device { 234 | public: 235 | LibremKey(); 236 | }; 237 | 238 | } 239 | } 240 | #endif 241 | -------------------------------------------------------------------------------- /libnitrokey/dissect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | /* 23 | * Protocol packet dissection 24 | */ 25 | #ifndef DISSECT_H 26 | #define DISSECT_H 27 | #include 28 | #include 29 | #include 30 | #include "misc.h" 31 | #include "cxx_semantics.h" 32 | #include "command_id.h" 33 | #include "device_proto.h" 34 | 35 | namespace nitrokey { 36 | namespace proto { 37 | 38 | template 39 | class QueryDissector : semantics::non_constructible { 40 | public: 41 | static std::string dissect(const HIDPacket &pod) { 42 | std::stringstream out; 43 | 44 | #ifdef LOG_VOLATILE_DATA 45 | out << "Raw HID packet:" << std::endl; 46 | out << ::nitrokey::misc::hexdump(reinterpret_cast(&pod), sizeof pod); 47 | #endif 48 | 49 | out << "Contents:" << std::endl; 50 | out << "Command ID:\t" << commandid_to_string(static_cast(pod.command_id)) 51 | << std::endl; 52 | out << "CRC:\t" 53 | << std::hex << std::setw(2) << std::setfill('0') 54 | << pod.crc << std::endl; 55 | 56 | out << "Payload:" << std::endl; 57 | out << pod.payload.dissect(); 58 | return out.str(); 59 | } 60 | }; 61 | 62 | 63 | 64 | 65 | template 66 | class ResponseDissector : semantics::non_constructible { 67 | public: 68 | static std::string status_translate_device(int status){ 69 | auto enum_status = static_cast(status); 70 | switch (enum_status){ 71 | case stick10::device_status::ok: return "OK"; 72 | case stick10::device_status::busy: return "BUSY"; 73 | case stick10::device_status::error: return "ERROR"; 74 | case stick10::device_status::received_report: return "RECEIVED_REPORT"; 75 | } 76 | return std::string("UNKNOWN: ") + std::to_string(status); 77 | } 78 | 79 | static std::string to_upper(std::string str){ 80 | for (auto & c: str) c = toupper(c); 81 | return str; 82 | } 83 | static std::string status_translate_command(int status){ 84 | auto enum_status = static_cast(status); 85 | switch (enum_status) { 86 | #define p(X) case X: return to_upper(std::string(#X)); 87 | p(stick10::command_status::ok) 88 | p(stick10::command_status::wrong_CRC) 89 | p(stick10::command_status::wrong_slot) 90 | p(stick10::command_status::slot_not_programmed) 91 | p(stick10::command_status::wrong_password) 92 | p(stick10::command_status::not_authorized) 93 | p(stick10::command_status::timestamp_warning) 94 | p(stick10::command_status::no_name_error) 95 | p(stick10::command_status::not_supported) 96 | p(stick10::command_status::unknown_command) 97 | p(stick10::command_status::AES_dec_failed) 98 | #undef p 99 | } 100 | return std::string("UNKNOWN: ") + std::to_string(status); 101 | } 102 | 103 | static std::string dissect(const HIDPacket &pod) { 104 | std::stringstream out; 105 | 106 | // FIXME use values from firmware (possibly generate separate 107 | // header automatically) 108 | 109 | #ifdef LOG_VOLATILE_DATA 110 | out << "Raw HID packet:" << std::endl; 111 | out << ::nitrokey::misc::hexdump(reinterpret_cast(&pod), sizeof pod); 112 | #endif 113 | 114 | out << "Device status:\t" << pod.device_status + 0 << " " 115 | << status_translate_device(pod.device_status) << std::endl; 116 | out << "Command ID:\t" << commandid_to_string(static_cast(pod.command_id)) << " hex: " << std::hex << static_cast(pod.command_id) 117 | << std::endl; 118 | out << "Last command CRC:\t" 119 | << std::hex << std::setw(2) << std::setfill('0') 120 | << pod.last_command_crc << std::endl; 121 | out << "Last command status:\t" << pod.last_command_status + 0 << " " 122 | << status_translate_command(pod.last_command_status) << std::endl; 123 | out << "CRC:\t" 124 | << std::hex << std::setw(2) << std::setfill('0') 125 | << pod.crc << std::endl; 126 | if(static_cast(pod.command_id) == pod.storage_status.command_id){ 127 | out << "Storage stick status (where applicable):" << std::endl; 128 | #define d(x) out << " "#x": \t"<< std::hex << std::setw(2) \ 129 | << std::setfill('0')<< static_cast(x) << std::endl; 130 | d(pod.storage_status.command_counter); 131 | d(pod.storage_status.command_id); 132 | d(pod.storage_status.device_status); 133 | d(pod.storage_status.progress_bar_value); 134 | #undef d 135 | } 136 | 137 | out << "Payload:" << std::endl; 138 | out << pod.payload.dissect(); 139 | return out.str(); 140 | } 141 | }; 142 | } 143 | } 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /libnitrokey/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef LOG_H 23 | #define LOG_H 24 | 25 | #include 26 | #include 27 | 28 | namespace nitrokey { 29 | namespace log { 30 | 31 | //for MSVC 32 | #ifdef ERROR 33 | #undef ERROR 34 | #endif 35 | 36 | 37 | enum class Loglevel : int { 38 | ERROR, 39 | WARNING, 40 | INFO, 41 | DEBUG_L1, 42 | DEBUG, 43 | DEBUG_L2 44 | }; 45 | 46 | class LogHandler { 47 | public: 48 | virtual void print(const std::string &, Loglevel lvl) = 0; 49 | virtual ~LogHandler() = default; 50 | protected: 51 | std::string loglevel_to_str(Loglevel); 52 | std::string format_message_to_string(const std::string &str, const Loglevel &lvl); 53 | 54 | }; 55 | 56 | class StdlogHandler : public LogHandler { 57 | public: 58 | virtual void print(const std::string &, Loglevel lvl) override; 59 | }; 60 | 61 | class FunctionalLogHandler : public LogHandler { 62 | using log_function_type = std::function; 63 | log_function_type log_function; 64 | public: 65 | FunctionalLogHandler(log_function_type _log_function); 66 | virtual void print(const std::string &, Loglevel lvl) override; 67 | 68 | }; 69 | 70 | class RawFunctionalLogHandler : public LogHandler { 71 | using log_function_type = std::function; 72 | log_function_type log_function; 73 | public: 74 | RawFunctionalLogHandler(log_function_type _log_function); 75 | virtual void print(const std::string &, Loglevel lvl) override; 76 | 77 | }; 78 | 79 | extern StdlogHandler stdlog_handler; 80 | 81 | class Log { 82 | public: 83 | Log() : mp_loghandler(&stdlog_handler), m_loglevel(Loglevel::WARNING) {} 84 | 85 | static Log &instance() { 86 | if (mp_instance == nullptr) mp_instance = new Log; 87 | return *mp_instance; 88 | } 89 | 90 | void operator()(const std::string &, Loglevel); 91 | void set_loglevel(Loglevel lvl) { m_loglevel = lvl; } 92 | void set_handler(LogHandler *handler) { mp_loghandler = handler; } 93 | 94 | private: 95 | LogHandler *mp_loghandler; 96 | Loglevel m_loglevel; 97 | static std::string prefix; 98 | public: 99 | static void setPrefix(std::string prefix = std::string()); 100 | 101 | private: 102 | 103 | static Log *mp_instance; 104 | }; 105 | } 106 | } 107 | 108 | 109 | #ifdef NO_LOG 110 | #define LOG(string, level) while(false){} 111 | #define LOGD(string) while(false){} 112 | #define LOGD1(string) while(false){} 113 | #else 114 | #define LOG(string, level) nitrokey::log::Log::instance()((string), (level)) 115 | #define LOGD1(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L1)) 116 | #define LOGD(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L2)) 117 | #endif 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /libnitrokey/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | 23 | #ifndef MISC_H 24 | #define MISC_H 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "log.h" 30 | #include "LibraryException.h" 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | namespace nitrokey { 37 | namespace misc { 38 | 39 | /** 40 | * Simple replacement for std::optional (C++17). 41 | */ 42 | template 43 | class Option { 44 | public: 45 | Option() : m_hasValue(false), m_value() {} 46 | Option(T value) : m_hasValue(true), m_value(value) {} 47 | 48 | bool has_value() const { 49 | return m_hasValue; 50 | } 51 | T value() const { 52 | if (!m_hasValue) { 53 | throw std::logic_error("Called Option::value without value"); 54 | } 55 | return m_value; 56 | } 57 | 58 | private: 59 | bool m_hasValue; 60 | T m_value; 61 | }; 62 | 63 | template 64 | std::string toHex(T value){ 65 | using namespace std; 66 | std::ostringstream oss; 67 | oss << std::hex << std::setw(sizeof(value)*2) << std::setfill('0') << value; 68 | return oss.str(); 69 | } 70 | 71 | #define FIELD_WIDTH_MAX (100) 72 | /** 73 | * Copies string from pointer to fixed size C-style array. Src needs to be a valid C-string - eg. ended with '\0'. 74 | * Throws when source is bigger than destination. 75 | * @tparam T type of destination array 76 | * @param dest fixed size destination array 77 | * @param src pointer to source c-style valid string 78 | */ 79 | template 80 | void strcpyT(T& dest, const char* src){ 81 | 82 | if (src == nullptr) 83 | // throw EmptySourceStringException(slot_number); 84 | return; 85 | const size_t s_dest = sizeof dest; 86 | const size_t src_strlen = strnlen(src, FIELD_WIDTH_MAX); 87 | LOG(std::string("strcpyT sizes dest src ") 88 | + std::to_string(s_dest) + " " 89 | + std::to_string(src_strlen) + " " 90 | , nitrokey::log::Loglevel::DEBUG_L2); 91 | if (src_strlen > s_dest){ 92 | throw TooLongStringException(src_strlen, s_dest, src); 93 | } 94 | strncpy(reinterpret_cast(&dest), src, s_dest); 95 | } 96 | 97 | #define bzero(b,len) (memset((b), '\0', (len)), (void) 0) 98 | template 99 | typename T::CommandPayload get_payload(){ 100 | //Create, initialize and return by value command payload 101 | typename T::CommandPayload st; 102 | bzero(&st, sizeof(st)); 103 | return st; 104 | } 105 | 106 | template 107 | void execute_password_command(Tdev &stick, const char *password) { 108 | auto p = get_payload(); 109 | p.set_defaults(); 110 | strcpyT(p.password, password); 111 | CMDTYPE::CommandTransaction::run(stick, p); 112 | } 113 | 114 | std::string hexdump(const uint8_t *p, size_t size, bool print_header=true, bool print_ascii=true, 115 | bool print_empty=true); 116 | uint32_t stm_crc32(const uint8_t *data, size_t size); 117 | std::vector hex_string_to_byte(const char* hexString); 118 | } 119 | } 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /libnitrokey/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef LIBNITROKEY_VERSION_H 23 | #define LIBNITROKEY_VERSION_H 24 | 25 | namespace nitrokey { 26 | unsigned int get_major_library_version(); 27 | 28 | unsigned int get_minor_library_version(); 29 | 30 | const char* get_library_version(); 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /log.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "log.h" 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | namespace nitrokey { 30 | namespace log { 31 | 32 | Log *Log::mp_instance = nullptr; 33 | StdlogHandler stdlog_handler; 34 | 35 | std::string Log::prefix = ""; 36 | 37 | 38 | std::string LogHandler::loglevel_to_str(Loglevel lvl) { 39 | switch (lvl) { 40 | case Loglevel::DEBUG_L1: 41 | return std::string("DEBUG_L1"); 42 | case Loglevel::DEBUG_L2: 43 | return std::string("DEBUG_L2"); 44 | case Loglevel::DEBUG: 45 | return std::string("DEBUG"); 46 | case Loglevel::INFO: 47 | return std::string("INFO"); 48 | case Loglevel::WARNING: 49 | return std::string("WARNING"); 50 | case Loglevel::ERROR: 51 | return std::string("ERROR"); 52 | } 53 | return std::string(""); 54 | } 55 | 56 | void Log::operator()(const std::string &logstr, Loglevel lvl) { 57 | if (mp_loghandler != nullptr){ 58 | // FIXME crashes on exit because static object under mp_loghandler is not valid anymore, see NitrokeyManager::set_log_function 59 | if (static_cast(lvl) <= static_cast(m_loglevel)) mp_loghandler->print(prefix+logstr, lvl); 60 | } 61 | } 62 | 63 | void Log::setPrefix(const std::string prefix) { 64 | if (!prefix.empty()){ 65 | Log::prefix = "["+prefix+"]"; 66 | } else { 67 | Log::prefix = ""; 68 | } 69 | } 70 | 71 | void StdlogHandler::print(const std::string &str, Loglevel lvl) { 72 | std::string s = format_message_to_string(str, lvl); 73 | std::clog << s; 74 | } 75 | 76 | void FunctionalLogHandler::print(const std::string &str, Loglevel lvl) { 77 | std::string s = format_message_to_string(str, lvl); 78 | log_function(s); 79 | } 80 | 81 | void RawFunctionalLogHandler::print(const std::string &str, Loglevel lvl) { 82 | log_function(str, lvl); 83 | } 84 | 85 | std::string LogHandler::format_message_to_string(const std::string &str, const Loglevel &lvl) { 86 | static bool last_short = false; 87 | if (str.length() == 1){ 88 | last_short = true; 89 | return str; 90 | } 91 | time_t t = time(nullptr); 92 | tm tm = *localtime(&t); 93 | 94 | std::stringstream s; 95 | s 96 | << (last_short? "\n" : "") 97 | << "[" << std::put_time(&tm, "%c") << "]" 98 | << "[" << loglevel_to_str(lvl) << "]\t" 99 | << str << std::endl; 100 | last_short = false; 101 | return s.str(); 102 | } 103 | 104 | FunctionalLogHandler::FunctionalLogHandler(log_function_type _log_function) { 105 | log_function = _log_function; 106 | } 107 | 108 | RawFunctionalLogHandler::RawFunctionalLogHandler(log_function_type _log_function) { 109 | log_function = _log_function; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'libnitrokey', 'cpp', 3 | version : '3.7.0', 4 | license : 'LGPL-3.0+', 5 | default_options : [ 6 | 'cpp_std=c++14' 7 | ], 8 | meson_version : '>= 0.48.0', 9 | ) 10 | cxx = meson.get_compiler('cpp') 11 | host_system = host_machine.system() 12 | pkg = import('pkgconfig') 13 | 14 | common_flags = [ 15 | '-Wno-unused-function', 16 | '-Wcast-qual', 17 | ] 18 | test_cxxflags = common_flags + [ 19 | '-Woverloaded-virtual', 20 | ] 21 | test_cflags = common_flags 22 | add_project_arguments(cxx.get_supported_arguments(test_cxxflags), language : 'cpp') 23 | if get_option('offline-tests') 24 | add_languages('c', required: get_option('offline-tests')) 25 | c = meson.get_compiler('c') 26 | add_project_arguments(c.get_supported_arguments(test_cflags), language : 'c') 27 | endif 28 | 29 | if target_machine.system() == 'freebsd' 30 | dep_hidapi = dependency('hidapi') 31 | else 32 | dep_hidapi = dependency('hidapi-libusb') 33 | endif 34 | 35 | inc_libnitrokey = include_directories('libnitrokey') 36 | libnitrokey_args = [] 37 | if not get_option('log') 38 | libnitrokey_args += ['-DNO_LOG'] 39 | endif 40 | if get_option('log-volatile-data') 41 | libnitrokey_args += ['-DLOG_VOLATILE_DATA'] 42 | endif 43 | 44 | version_array = meson.project_version().split('.') 45 | version_major = version_array[0].to_int() 46 | version_minor = version_array[1].to_int() 47 | version_data = configuration_data() 48 | version_data.set('PROJECT_VERSION_MAJOR', version_major) 49 | version_data.set('PROJECT_VERSION_MINOR', version_minor) 50 | # We don't want to substitute it by noop 51 | version_data.set('PROJECT_VERSION_GIT', '@VCS_TAG@') 52 | version_cc_in = configure_file( 53 | input : 'version.cc.in', 54 | output : 'version.cc.in', 55 | configuration : version_data, 56 | ) 57 | version_cc = vcs_tag( 58 | input : version_cc_in, 59 | output : 'version.cc', 60 | fallback : 'v@0@'.format(meson.project_version()), 61 | ) 62 | libnitrokey = library( 63 | 'nitrokey', 64 | sources : [ 65 | 'command_id.cc', 66 | 'device.cc', 67 | 'log.cc', 68 | version_cc, 69 | 'misc.cc', 70 | 'NitrokeyManager.cc', 71 | 'NK_C_API.cc', 72 | 'DeviceCommunicationExceptions.cpp', 73 | ], 74 | include_directories : [ 75 | inc_libnitrokey, 76 | ], 77 | dependencies : [ 78 | dep_hidapi, 79 | ], 80 | cpp_args : libnitrokey_args, 81 | version : meson.project_version(), 82 | install : true, 83 | ) 84 | install_headers( 85 | 'libnitrokey/CommandFailedException.h', 86 | 'libnitrokey/command.h', 87 | 'libnitrokey/command_id.h', 88 | 'libnitrokey/cxx_semantics.h', 89 | 'libnitrokey/DeviceCommunicationExceptions.h', 90 | 'libnitrokey/device.h', 91 | 'libnitrokey/device_proto.h', 92 | 'libnitrokey/dissect.h', 93 | 'libnitrokey/LibraryException.h', 94 | 'libnitrokey/log.h', 95 | 'libnitrokey/LongOperationInProgressException.h', 96 | 'libnitrokey/misc.h', 97 | 'libnitrokey/version.h', 98 | 'libnitrokey/NitrokeyManager.h', 99 | 'libnitrokey/stick10_commands_0.8.h', 100 | 'libnitrokey/stick10_commands.h', 101 | 'libnitrokey/stick20_commands.h', 102 | subdir : meson.project_name(), 103 | ) 104 | 105 | ext_libnitrokey = declare_dependency( 106 | link_with : libnitrokey, 107 | include_directories : inc_libnitrokey, 108 | ) 109 | 110 | pkg.generate( 111 | name : meson.project_name(), 112 | filebase : 'libnitrokey-1', 113 | libraries : libnitrokey, 114 | version : meson.project_version(), 115 | requires_private : dep_hidapi.name(), 116 | description : 'Library for communicating with Nitrokey in a clean and easy manner', 117 | ) 118 | 119 | if target_machine.system() == 'freebsd' 120 | dep_udev = dependency('libudev') 121 | else 122 | dep_udev = dependency('udev') 123 | endif 124 | install_data( 125 | 'data/41-nitrokey.rules', 126 | install_dir : '@0@/rules.d'.format(dep_udev.get_pkgconfig_variable('udevdir')), 127 | ) 128 | 129 | if get_option('tests') or get_option('offline-tests') 130 | dep_catch = dependency('catch2', version : '>=2.3.0', required : false) 131 | if not dep_catch.found() 132 | dep_catch = declare_dependency( 133 | include_directories : include_directories('unittest/Catch/single_include') 134 | ) 135 | endif 136 | _catch = static_library( 137 | 'catch', 138 | sources : [ 139 | 'unittest/catch_main.cpp', 140 | ], 141 | dependencies : [ 142 | dep_catch, 143 | ], 144 | ) 145 | _dep_catch = declare_dependency( 146 | link_with : _catch, 147 | dependencies : dep_catch, 148 | ) 149 | endif 150 | 151 | tests = [] 152 | if get_option('offline-tests') 153 | tests += [ 154 | ['test_offline', 'test_offline.cc'], 155 | ['test_minimal', 'test_minimal.c'], 156 | ] 157 | endif 158 | if get_option('tests') 159 | tests += [ 160 | ['test_C_API', 'test_C_API.cpp'], 161 | ['test1', 'test1.cc'], 162 | ['test2', 'test2.cc'], 163 | ['test3', 'test3.cc'], 164 | ['test_HOTP', 'test_HOTP.cc'], 165 | ['test_memory', 'test_memory.c'], 166 | ['test_issues', 'test_issues.cc'], 167 | ['test_multiple_devices', 'test_multiple_devices.cc'], 168 | ['test_strdup', 'test_strdup.cpp'], 169 | ['test_safe', 'test_safe.cpp'], 170 | ] 171 | endif 172 | foreach tst : tests 173 | test( 174 | tst[0], 175 | executable( 176 | tst[0], 177 | sources : 'unittest/@0@'.format(tst[1]), 178 | dependencies : [ 179 | ext_libnitrokey, 180 | _dep_catch, 181 | ], 182 | ) 183 | ) 184 | endforeach 185 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('log', type : 'boolean', value : true, description : 'Logging functionality') 2 | option('log-volatile-data', type : 'boolean', value : false, description : 'Log volatile data (debug)') 3 | option('tests', type : 'boolean', value : false, description : 'Compile tests (needs connected PRO device)') 4 | option('offline-tests', type : 'boolean', value : false, description : 'Compile offline tests') 5 | -------------------------------------------------------------------------------- /misc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include 23 | #include 24 | #include "misc.h" 25 | #include "inttypes.h" 26 | #include 27 | #include 28 | #include "LibraryException.h" 29 | #include 30 | 31 | namespace nitrokey { 32 | namespace misc { 33 | 34 | 35 | 36 | ::std::vector hex_string_to_byte(const char* hexString){ 37 | const size_t big_string_size = 257; //arbitrary 'big' number 38 | const size_t s_size = strnlen(hexString, big_string_size); 39 | const size_t d_size = s_size/2; 40 | if (s_size%2!=0 || s_size>=big_string_size){ 41 | throw InvalidHexString(0); 42 | } 43 | auto data = ::std::vector(); 44 | data.reserve(d_size); 45 | 46 | char buf[3]; 47 | buf[2] = '\0'; 48 | for(size_t i=0; i 64 | ::std::string hexdump(const uint8_t *p, size_t size, bool print_header, 65 | bool print_ascii, bool print_empty) { 66 | ::std::stringstream out; 67 | char formatbuf[128]; 68 | const uint8_t *pstart = p; 69 | 70 | for (const uint8_t *pend = p + size; p < pend;) { 71 | if (print_header){ 72 | snprintf(formatbuf, 128, "%04x\t", static_cast (p - pstart)); 73 | out << formatbuf; 74 | } 75 | 76 | const uint8_t* pp = p; 77 | for (const uint8_t *le = p + 16; p < le; p++) { 78 | if (p < pend){ 79 | snprintf(formatbuf, 128, "%02x ", uint8_t(*p)); 80 | out << formatbuf; 81 | } else { 82 | if(print_empty) 83 | out << "-- "; 84 | } 85 | 86 | } 87 | if(print_ascii){ 88 | out << " "; 89 | for (const uint8_t *le = pp + 16; pp < le && pp < pend; pp++) { 90 | if (std::isgraph(*pp)) 91 | out << uint8_t(*pp); 92 | else 93 | out << '.'; 94 | } 95 | } 96 | out << ::std::endl; 97 | } 98 | return out.str(); 99 | } 100 | 101 | static uint32_t _crc32(uint32_t crc, uint32_t data) { 102 | int i; 103 | crc = crc ^ data; 104 | 105 | for (i = 0; i < 32; i++) { 106 | if (crc & 0x80000000) 107 | crc = (crc << 1) ^ 0x04C11DB7; // polynomial used in STM32 108 | else 109 | crc = (crc << 1); 110 | } 111 | 112 | return crc; 113 | } 114 | 115 | uint32_t stm_crc32(const uint8_t *data, size_t size) { 116 | uint32_t crc = 0xffffffff; 117 | const uint32_t *pend = reinterpret_cast(data + size); 118 | for (const uint32_t *p = reinterpret_cast(data); p < pend; p++) 119 | crc = _crc32(crc, *p); 120 | return crc; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /python3_bindings_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Copyright (c) 2015-2018 Nitrokey UG 4 | 5 | This file is part of libnitrokey. 6 | 7 | libnitrokey is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | any later version. 11 | 12 | libnitrokey is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with libnitrokey. If not, see . 19 | 20 | SPDX-License-Identifier: LGPL-3.0 21 | """ 22 | 23 | import cffi 24 | from enum import Enum 25 | 26 | """ 27 | This example will print 10 HOTP codes from just written HOTP#2 slot. 28 | For more examples of use please refer to unittest/test_*.py files. 29 | """ 30 | 31 | ffi = cffi.FFI() 32 | get_string = ffi.string 33 | 34 | class DeviceErrorCode(Enum): 35 | STATUS_OK = 0 36 | NOT_PROGRAMMED = 3 37 | WRONG_PASSWORD = 4 38 | STATUS_NOT_AUTHORIZED = 5 39 | STATUS_AES_DEC_FAILED = 0xa 40 | 41 | 42 | def get_library(): 43 | fp = 'NK_C_API.h' # path to C API header 44 | 45 | declarations = [] 46 | with open(fp, 'r') as f: 47 | declarations = f.readlines() 48 | 49 | cnt = 0 50 | a = iter(declarations) 51 | for declaration in a: 52 | if declaration.strip().startswith('NK_C_API'): 53 | declaration = declaration.replace('NK_C_API', '').strip() 54 | while ';' not in declaration: 55 | declaration += (next(a)).strip() 56 | # print(declaration) 57 | ffi.cdef(declaration, override=True) 58 | cnt +=1 59 | print('Imported {} declarations'.format(cnt)) 60 | 61 | 62 | C = None 63 | import os, sys 64 | path_build = os.path.join(".", "build") 65 | paths = [ 66 | os.environ.get('LIBNK_PATH', None), 67 | os.path.join(path_build,"libnitrokey.so"), 68 | os.path.join(path_build,"libnitrokey.dylib"), 69 | os.path.join(path_build,"libnitrokey.dll"), 70 | os.path.join(path_build,"nitrokey.dll"), 71 | ] 72 | for p in paths: 73 | if not p: continue 74 | print("Trying " +p) 75 | p = os.path.abspath(p) 76 | if os.path.exists(p): 77 | print("Found: "+p) 78 | C = ffi.dlopen(p) 79 | break 80 | else: 81 | print("File does not exist: " + p) 82 | if not C: 83 | print("No library file found") 84 | print("Please set the path using LIBNK_PATH environment variable to existing library or compile it (see " 85 | "README.md for details)") 86 | sys.exit(1) 87 | 88 | return C 89 | 90 | 91 | def get_hotp_code(lib, i): 92 | return get_string(lib.NK_get_hotp_code(i)) 93 | 94 | def to_hex(ss): 95 | return ''.join([ format(ord(s),'02x') for s in ss ]) 96 | 97 | print('Warning!') 98 | print('This example will change your configuration on inserted stick and overwrite your HOTP#2 slot.') 99 | print('Please write "continue" to continue or any other string to quit') 100 | a = input() 101 | 102 | if not a == 'continue': 103 | exit() 104 | 105 | ADMIN = input('Please enter your admin PIN (empty string uses 12345678) ') 106 | ADMIN = ADMIN or '12345678' # use default if empty string 107 | 108 | show_log = input('Should log messages be shown (please write "yes" to enable; this will make harder reading script output) ') == 'yes' 109 | libnitrokey = get_library() 110 | 111 | if show_log: 112 | log_level = input('Please select verbosity level (0-5, 2 is library default, 3 will be selected on empty input) ') 113 | log_level = log_level or '3' 114 | log_level = int(log_level) 115 | libnitrokey.NK_set_debug_level(log_level) 116 | else: 117 | libnitrokey.NK_set_debug_level(2) 118 | 119 | 120 | ADMIN_TEMP = '123123123' 121 | RFC_SECRET = to_hex('12345678901234567890') 122 | 123 | # libnitrokey.NK_login('S') # connect only to Nitrokey Storage device 124 | # libnitrokey.NK_login('P') # connect only to Nitrokey Pro device 125 | device_connected = libnitrokey.NK_login_auto() # connect to any Nitrokey Stick 126 | if device_connected: 127 | print('Connected to Nitrokey device!') 128 | else: 129 | print('Could not connect to Nitrokey device!') 130 | exit() 131 | 132 | use_8_digits = True 133 | pin_correct = libnitrokey.NK_first_authenticate(ADMIN.encode('ascii'), ADMIN_TEMP.encode('ascii')) == DeviceErrorCode.STATUS_OK.value 134 | if pin_correct: 135 | print('Your PIN is correct!') 136 | else: 137 | print('Your PIN is not correct! Please try again. Please be careful to not lock your stick!') 138 | retry_count_left = libnitrokey.NK_get_admin_retry_count() 139 | print('Retry count left: %d' % retry_count_left ) 140 | exit() 141 | 142 | # For function parameters documentation please check NK_C_API.h 143 | assert libnitrokey.NK_write_config(255, 255, 255, False, True, ADMIN_TEMP.encode('ascii')) == DeviceErrorCode.STATUS_OK.value 144 | libnitrokey.NK_first_authenticate(ADMIN.encode('ascii'), ADMIN_TEMP.encode('ascii')) 145 | libnitrokey.NK_write_hotp_slot(1, 'python_test'.encode('ascii'), RFC_SECRET.encode('ascii'), 0, use_8_digits, False, False, "".encode('ascii'), 146 | ADMIN_TEMP.encode('ascii')) 147 | # RFC test according to: https://tools.ietf.org/html/rfc4226#page-32 148 | test_data = [ 149 | 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871, 150 | 645520489, 151 | ] 152 | print('Getting HOTP code from Nitrokey Stick (RFC test, 8 digits): ') 153 | for i in range(10): 154 | hotp_slot_1_code = get_hotp_code(libnitrokey, 1) 155 | correct_str = "correct!" if hotp_slot_1_code.decode('ascii') == str(test_data[i])[-8:] else "not correct" 156 | print('%d: %s, should be %s -> %s' % (i, hotp_slot_1_code.decode('ascii'), str(test_data[i])[-8:], correct_str)) 157 | libnitrokey.NK_logout() # disconnect device 158 | -------------------------------------------------------------------------------- /python_bindings_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | Copyright (c) 2015-2018 Nitrokey UG 4 | 5 | This file is part of libnitrokey. 6 | 7 | libnitrokey is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | any later version. 11 | 12 | libnitrokey is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with libnitrokey. If not, see . 19 | 20 | SPDX-License-Identifier: LGPL-3.0 21 | """ 22 | 23 | import cffi 24 | from enum import Enum 25 | 26 | """ 27 | This example will print 10 HOTP codes from just written HOTP#2 slot. 28 | For more examples of use please refer to unittest/test_*.py files. 29 | """ 30 | 31 | ffi = cffi.FFI() 32 | get_string = ffi.string 33 | 34 | class DeviceErrorCode(Enum): 35 | STATUS_OK = 0 36 | NOT_PROGRAMMED = 3 37 | WRONG_PASSWORD = 4 38 | STATUS_NOT_AUTHORIZED = 5 39 | STATUS_AES_DEC_FAILED = 0xa 40 | 41 | 42 | def get_library(): 43 | fp = 'NK_C_API.h' # path to C API header 44 | 45 | declarations = [] 46 | with open(fp, 'r') as f: 47 | declarations = f.readlines() 48 | 49 | cnt = 0 50 | a = iter(declarations) 51 | for declaration in a: 52 | if declaration.strip().startswith('NK_C_API'): 53 | declaration = declaration.replace('NK_C_API', '').strip() 54 | while ';' not in declaration: 55 | declaration += (next(a)).strip() 56 | # print(declaration) 57 | ffi.cdef(declaration, override=True) 58 | cnt +=1 59 | print('Imported {} declarations'.format(cnt)) 60 | 61 | 62 | C = None 63 | import os, sys 64 | path_build = os.path.join(".", "build") 65 | paths = [ 66 | os.environ.get('LIBNK_PATH', None), 67 | os.path.join(path_build,"libnitrokey.so"), 68 | os.path.join(path_build,"libnitrokey.dylib"), 69 | os.path.join(path_build,"libnitrokey.dll"), 70 | os.path.join(path_build,"nitrokey.dll"), 71 | ] 72 | for p in paths: 73 | if not p: continue 74 | print("Trying " +p) 75 | p = os.path.abspath(p) 76 | if os.path.exists(p): 77 | print("Found: "+p) 78 | C = ffi.dlopen(p) 79 | break 80 | else: 81 | print("File does not exist: " + p) 82 | if not C: 83 | print("No library file found") 84 | print("Please set the path using LIBNK_PATH environment variable to existing library or compile it (see " 85 | "README.md for details)") 86 | sys.exit(1) 87 | 88 | return C 89 | 90 | 91 | def get_hotp_code(lib, i): 92 | return get_string(lib.NK_get_hotp_code(i)) 93 | 94 | def to_hex(ss): 95 | return ''.join([ format(ord(s),'02x') for s in ss ]) 96 | 97 | print('Warning!') 98 | print('This example will change your configuration on inserted stick and overwrite your HOTP#2 slot.') 99 | print('Please write "continue" to continue or any other string to quit') 100 | a = raw_input() 101 | 102 | if not a == 'continue': 103 | exit() 104 | 105 | ADMIN = raw_input('Please enter your admin PIN (empty string uses 12345678) ') 106 | ADMIN = ADMIN or '12345678' # use default if empty string 107 | 108 | show_log = raw_input('Should log messages be shown (please write "yes" to enable; this will make harder reading script output) ') == 'yes' 109 | libnitrokey = get_library() 110 | 111 | if show_log: 112 | log_level = raw_input('Please select verbosity level (0-5, 2 is library default, 3 will be selected on empty input) ') 113 | log_level = log_level or '3' 114 | log_level = int(log_level) 115 | libnitrokey.NK_set_debug_level(log_level) 116 | else: 117 | libnitrokey.NK_set_debug_level(2) 118 | 119 | 120 | ADMIN_TEMP = '123123123' 121 | RFC_SECRET = to_hex('12345678901234567890') 122 | 123 | # libnitrokey.NK_login('S') # connect only to Nitrokey Storage device 124 | # libnitrokey.NK_login('P') # connect only to Nitrokey Pro device 125 | device_connected = libnitrokey.NK_login_auto() # connect to any Nitrokey Stick 126 | if device_connected: 127 | print('Connected to Nitrokey device!') 128 | else: 129 | print('Could not connect to Nitrokey device!') 130 | exit() 131 | use_8_digits = True 132 | pin_correct = libnitrokey.NK_first_authenticate(ADMIN, ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 133 | if pin_correct: 134 | print('Your PIN is correct!') 135 | else: 136 | print('Your PIN is not correct! Please try again. Please be careful to not lock your stick!') 137 | retry_count_left = libnitrokey.NK_get_admin_retry_count() 138 | print('Retry count left: %d' % retry_count_left ) 139 | exit() 140 | 141 | # For function parameters documentation please check NK_C_API.h 142 | assert libnitrokey.NK_write_config(255, 255, 255, False, True, ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 143 | libnitrokey.NK_first_authenticate(ADMIN, ADMIN_TEMP) 144 | libnitrokey.NK_write_hotp_slot(1, 'python_test', RFC_SECRET, 0, use_8_digits, False, False, "", 145 | ADMIN_TEMP) 146 | # RFC test according to: https://tools.ietf.org/html/rfc4226#page-32 147 | test_data = [ 148 | 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871, 149 | 645520489, 150 | ] 151 | print('Getting HOTP code from Nitrokey Stick (RFC test, 8 digits): ') 152 | for i in range(10): 153 | hotp_slot_1_code = get_hotp_code(libnitrokey, 1) 154 | correct_str = "correct!" if hotp_slot_1_code == str(test_data[i])[-8:] else "not correct" 155 | print('%d: %s, should be %s -> %s' % (i, hotp_slot_1_code, str(test_data[i])[-8:], correct_str)) 156 | libnitrokey.NK_logout() # disconnect device 157 | -------------------------------------------------------------------------------- /unittest/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | cffi = "*" 10 | pytest-repeat = "*" 11 | pytest-randomly = "*" 12 | tqdm = "*" 13 | oath = "*" 14 | pyexpect = "*" 15 | pytest = "*" 16 | pytest-reporter-html1 = "*" 17 | pytest-reporter = "*" 18 | hypothesis = "*" 19 | 20 | [requires] 21 | python_version = "3.11" 22 | -------------------------------------------------------------------------------- /unittest/build/libnitrokey.so: -------------------------------------------------------------------------------- 1 | ../../build/libnitrokey.so -------------------------------------------------------------------------------- /unittest/build/run.sh: -------------------------------------------------------------------------------- 1 | LD_LIBRARY_PATH=. ./test_HOTP 2 | -------------------------------------------------------------------------------- /unittest/catch_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() 23 | #include "catch2/catch.hpp" -------------------------------------------------------------------------------- /unittest/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2015-2019 Nitrokey UG 3 | 4 | This file is part of libnitrokey. 5 | 6 | libnitrokey is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | libnitrokey is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with libnitrokey. If not, see . 18 | 19 | SPDX-License-Identifier: LGPL-3.0 20 | """ 21 | 22 | import pytest 23 | import os, sys 24 | 25 | from misc import ffi, gs 26 | 27 | device_type = None 28 | 29 | from logging import getLogger, basicConfig, DEBUG 30 | 31 | basicConfig(format='* %(relativeCreated)6d %(filename)s:%(lineno)d %(message)s',level=DEBUG) 32 | log = getLogger('conftest') 33 | print = log.debug 34 | 35 | def get_device_type(): 36 | return device_type 37 | 38 | 39 | def skip_if_device_version_lower_than(allowed_devices): 40 | global device_type 41 | model, version = device_type 42 | infinite_version_number = 999 43 | if allowed_devices.get(model, infinite_version_number) > version: 44 | pytest.skip('This device model is not applicable to run this test') 45 | 46 | 47 | class AtrrCallProx(object): 48 | def __init__(self, C, name): 49 | self.C = C 50 | self.name = name 51 | 52 | def __call__(self, *args, **kwargs): 53 | print('Calling {}{}'.format(self.name, args)) 54 | res = self.C(*args, **kwargs) 55 | res_s = res 56 | try: 57 | res_s = '{} => '.format(res) + '{}'.format(gs(res)) 58 | except Exception as e: 59 | pass 60 | print('Result of {}: {}'.format(self.name, res_s)) 61 | return res 62 | 63 | 64 | class AttrProxy(object): 65 | def __init__(self, C, name): 66 | self.C = C 67 | self.name = name 68 | 69 | def __getattr__(self, attr): 70 | return AtrrCallProx(getattr(self.C, attr), attr) 71 | 72 | 73 | @pytest.fixture(scope="module") 74 | def C_offline(request=None): 75 | print("Getting library without initializing connection") 76 | return get_library(request, allow_offline=True) 77 | 78 | 79 | @pytest.fixture(scope="session") 80 | def C(request=None): 81 | import platform 82 | print(f"Python version: {platform.python_version()}") 83 | print(f"OS: {platform.system()} {platform.release()} {platform.version()}") 84 | print("Getting library with connection initialized") 85 | return get_library(request) 86 | 87 | 88 | def get_library(request, allow_offline=False): 89 | library_read_declarations() 90 | C = library_open_lib() 91 | 92 | C.NK_set_debug_level(int(os.environ.get('LIBNK_DEBUG', 2))) 93 | 94 | nk_login = C.NK_login_auto() 95 | if nk_login != 1: 96 | print('No devices detected!') 97 | if not allow_offline: 98 | assert nk_login != 0 # returns 0 if not connected or wrong model or 1 when connected 99 | global device_type 100 | firmware_version = C.NK_get_minor_firmware_version() 101 | model = C.NK_get_device_model() 102 | model = 'P' if model == 1 else 'S' if model == 2 else 'U' 103 | device_type = (model, firmware_version) 104 | print('Connected library: {}'.format(gs(C.NK_get_library_version()))) 105 | print('Connected device: {} {}'.format(model, firmware_version)) 106 | 107 | # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 108 | # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK 109 | 110 | # C.NK_status() 111 | 112 | def fin(): 113 | print('\nFinishing connection to device') 114 | C.NK_logout() 115 | print('Finished') 116 | 117 | if request: 118 | request.addfinalizer(fin) 119 | # C.NK_set_debug(True) 120 | C.NK_set_debug_level(int(os.environ.get('LIBNK_DEBUG', 3))) 121 | 122 | return AttrProxy(C, "libnitrokey C") 123 | 124 | 125 | def library_open_lib(): 126 | C = None 127 | path_build = os.path.join("..", "build") 128 | paths = [ 129 | os.environ.get('LIBNK_PATH', None), 130 | os.path.join(path_build, "libnitrokey.so"), 131 | os.path.join(path_build, "libnitrokey.dylib"), 132 | os.path.join(path_build, "libnitrokey.dll"), 133 | os.path.join(path_build, "nitrokey.dll"), 134 | ] 135 | for p in paths: 136 | if not p: continue 137 | print("Trying " + p) 138 | p = os.path.abspath(p) 139 | if os.path.exists(p): 140 | print("Found: " + p) 141 | C = ffi.dlopen(p) 142 | break 143 | else: 144 | print("File does not exist: " + p) 145 | if not C: 146 | print("No library file found") 147 | sys.exit(1) 148 | return C 149 | 150 | 151 | def library_read_declarations(): 152 | fp = '../NK_C_API.h' 153 | declarations = [] 154 | with open(fp, 'r') as f: 155 | declarations = f.readlines() 156 | cnt = 0 157 | a = iter(declarations) 158 | for declaration in a: 159 | if declaration.strip().startswith('NK_C_API') \ 160 | or declaration.strip().startswith('struct'): 161 | declaration = declaration.replace('NK_C_API', '').strip() 162 | while ');' not in declaration and '};' not in declaration: 163 | declaration += (next(a)).strip() + '\n' 164 | ffi.cdef(declaration, override=True) 165 | cnt += 1 166 | print('Imported {} declarations'.format(cnt)) 167 | 168 | 169 | def pytest_addoption(parser): 170 | parser.addoption( 171 | "--runslow", action="store_true", default=False, help="run slow tests" 172 | ) 173 | parser.addoption("--run-skipped", action="store_true", 174 | help="run the tests skipped by default, e.g. adding side effects") 175 | 176 | def pytest_runtest_setup(item): 177 | if 'skip_by_default' in item.keywords and not item.config.getoption("--run-skipped"): 178 | pytest.skip("need --run-skipped option to run this test") 179 | 180 | def pytest_configure(config): 181 | config.addinivalue_line("markers", "slow: mark test as slow to run") 182 | 183 | 184 | 185 | def library_device_reconnect(C): 186 | C.NK_logout() 187 | C = library_open_lib() 188 | C.NK_logout() 189 | assert C.NK_login_auto() == 1, 'Device not found' 190 | return C -------------------------------------------------------------------------------- /unittest/constants.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2015-2018 Nitrokey UG 3 | 4 | This file is part of libnitrokey. 5 | 6 | libnitrokey is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | libnitrokey is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with libnitrokey. If not, see . 18 | 19 | SPDX-License-Identifier: LGPL-3.0 20 | """ 21 | from enum import Enum 22 | from sys import stderr 23 | 24 | from misc import to_hex, bb 25 | from conftest import print 26 | 27 | RFC_SECRET_HR = '12345678901234567890' 28 | RFC_SECRET = to_hex(RFC_SECRET_HR) # '31323334353637383930...' 29 | bbRFC_SECRET = bb(RFC_SECRET) 30 | 31 | 32 | # print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) ) 33 | 34 | class DefaultPasswords: 35 | ADMIN = b'12345678' 36 | USER = b'123456' 37 | ADMIN_TEMP = b'123123123' 38 | USER_TEMP = b'234234234' 39 | UPDATE = b'12345678' 40 | UPDATE_TEMP = b'123update123' 41 | UPDATE_LONG = b'1234567890'*2 42 | UPDATE_TOO_LONG = UPDATE_LONG + b'x' 43 | UPDATE_TOO_SHORT = UPDATE_LONG[:7] 44 | 45 | 46 | class DeviceErrorCode(Enum): 47 | STATUS_OK = 0 48 | BUSY = 1 # busy or busy progressbar in place of wrong_CRC status 49 | NOT_PROGRAMMED = 3 50 | WRONG_PASSWORD = 4 51 | STATUS_NOT_AUTHORIZED = 5 52 | STATUS_AES_DEC_FAILED = 10 53 | STATUS_WRONG_SLOT = 2 54 | STATUS_TIMESTAMP_WARNING = 6 55 | STATUS_NO_NAME_ERROR = 7 56 | STATUS_NOT_SUPPORTED = 8 57 | STATUS_UNKNOWN_COMMAND = 9 58 | STATUS_AES_CREATE_KEY_FAILED = 11 59 | STATUS_ERROR_CHANGING_USER_PASSWORD = 12 60 | STATUS_ERROR_CHANGING_ADMIN_PASSWORD = 13 61 | STATUS_ERROR_UNBLOCKING_PIN = 14 62 | 63 | STATUS_UNKNOWN_ERROR = 100 64 | STATUS_DISCONNECTED = 255 65 | 66 | def __eq__(self, other): 67 | other_name = 'Unknown' 68 | try: 69 | other_name = str(DeviceErrorCode(other).name) 70 | except: 71 | pass 72 | result = self.value == other 73 | print(f'Returned {other_name}, expected {self.name} => {result}') 74 | return result 75 | 76 | class LibraryErrors(Enum): 77 | TOO_LONG_STRING = 200 78 | INVALID_SLOT = 201 79 | INVALID_HEX_STRING = 202 80 | TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203 81 | 82 | def __eq__(self, other): 83 | other_name = 'Unknown' 84 | try: 85 | other_name = str(LibraryErrors(other).name) 86 | except: 87 | pass 88 | result = self.value == other 89 | print(f'Returned {other_name}, expected {self.name} => {result}') 90 | return result 91 | 92 | 93 | HOTP_slot_count = 3 94 | TOTP_slot_count = 15 95 | PWS_SLOT_COUNT = 16 96 | -------------------------------------------------------------------------------- /unittest/helpers.py: -------------------------------------------------------------------------------- 1 | from constants import DeviceErrorCode, PWS_SLOT_COUNT, DefaultPasswords 2 | from misc import gs, bb 3 | 4 | 5 | def helper_fill(str_to_fill, target_width): 6 | assert target_width >= len(str_to_fill) 7 | numbers = '1234567890' * 4 8 | str_to_fill += numbers[:target_width - len(str_to_fill)] 9 | assert len(str_to_fill) == target_width 10 | return bb(str_to_fill) 11 | 12 | 13 | def helper_PWS_get_pass(suffix): 14 | return helper_fill('pass' + suffix, 20) 15 | 16 | 17 | def helper_PWS_get_loginname(suffix): 18 | return helper_fill('login' + suffix, 32) 19 | 20 | 21 | def helper_PWS_get_slotname(suffix): 22 | return helper_fill('slotname' + suffix, 11) 23 | 24 | 25 | def helper_check_device_for_data(C): 26 | assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK 27 | assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK 28 | 29 | for i in range(0, PWS_SLOT_COUNT): 30 | iss = str(i) 31 | assert gs(C.NK_get_password_safe_slot_name(i)) == helper_PWS_get_slotname(iss) 32 | assert gs(C.NK_get_password_safe_slot_login(i)) == helper_PWS_get_loginname(iss) 33 | assert gs(C.NK_get_password_safe_slot_password(i)) == helper_PWS_get_pass(iss) 34 | return True 35 | 36 | 37 | def helper_populate_device(C): 38 | # FIXME use object with random data, and check against it 39 | # FIXME generate OTP as well, and check codes against its secrets 40 | assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK 41 | res = C.NK_enable_password_safe(DefaultPasswords.USER) 42 | if res != DeviceErrorCode.STATUS_OK: 43 | assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK 44 | assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK 45 | 46 | for i in range(0, PWS_SLOT_COUNT): 47 | iss = str(i) 48 | assert C.NK_write_password_safe_slot(i, 49 | helper_PWS_get_slotname(iss), helper_PWS_get_loginname(iss), 50 | helper_PWS_get_pass(iss)) == DeviceErrorCode.STATUS_OK 51 | return True 52 | -------------------------------------------------------------------------------- /unittest/libnk-tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Copyright (c) 2015-2019 Nitrokey UG 5 | 6 | This file is part of libnitrokey. 7 | 8 | libnitrokey is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | any later version. 12 | 13 | libnitrokey is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with libnitrokey. If not, see . 20 | 21 | SPDX-License-Identifier: LGPL-3.0 22 | """ 23 | 24 | import click 25 | 26 | from conftest import get_library, get_device_type 27 | from constants import DefaultPasswords, DeviceErrorCode 28 | 29 | 30 | @click.group() 31 | def cli(): 32 | pass 33 | 34 | 35 | @click.command() 36 | def update(): 37 | libnk = get_library(None) 38 | device_type = get_device_type() 39 | print(device_type) 40 | assert device_type[0] == 'S' 41 | assert libnk.NK_enable_firmware_update(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 42 | 43 | 44 | cli.add_command(update) 45 | 46 | if __name__ == '__main__': 47 | cli() 48 | -------------------------------------------------------------------------------- /unittest/misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2015-2018 Nitrokey UG 3 | 4 | This file is part of libnitrokey. 5 | 6 | libnitrokey is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | libnitrokey is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with libnitrokey. If not, see . 18 | 19 | SPDX-License-Identifier: LGPL-3.0 20 | """ 21 | 22 | import cffi 23 | 24 | ffi = cffi.FFI() 25 | gs = ffi.string 26 | 27 | 28 | def to_hex(s): 29 | return "".join("{:02x}".format(ord(c)) for c in s) 30 | 31 | 32 | def wait(t): 33 | import time 34 | msg = 'Waiting for %d seconds' % t 35 | print(msg.center(40, '=')) 36 | time.sleep(t) 37 | 38 | 39 | def cast_pointer_to_tuple(obj, typen, len): 40 | # usage: 41 | # config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5) 42 | return tuple(ffi.cast("%s [%d]" % (typen, len), obj)[0:len]) 43 | 44 | 45 | def get_devices_firmware_version(C): 46 | firmware = C.NK_get_minor_firmware_version() 47 | return firmware 48 | 49 | 50 | def is_pro_rtm_07(C): 51 | firmware = get_devices_firmware_version(C) 52 | return firmware == 7 53 | 54 | 55 | def is_pro_rtm_08(C): 56 | firmware = get_devices_firmware_version(C) 57 | return firmware in [8,9] 58 | 59 | 60 | def is_storage(C): 61 | """ 62 | exact firmware storage is sent by other function 63 | """ 64 | # TODO identify connected device directly 65 | firmware = get_devices_firmware_version(C) 66 | return firmware >= 45 67 | 68 | 69 | def is_long_OTP_secret_handled(C): 70 | return is_pro_rtm_08(C) or is_storage(C) and get_devices_firmware_version(C) >= 54 71 | 72 | 73 | def has_binary_counter(C): 74 | return (not is_storage(C)) or (is_storage(C) and get_devices_firmware_version(C) >= 54) 75 | 76 | 77 | def bb(x): 78 | return bytes(x, encoding='ascii') -------------------------------------------------------------------------------- /unittest/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pytest.ini_options] 2 | markers = [ 3 | "slow: marks tests as slow (deselect with '-m \"not slow\"')", 4 | "serial", 5 | "aes", 6 | "encrypted", 7 | "factory_reset", 8 | "firmware", 9 | "hidden", 10 | "info", 11 | "lock_device", 12 | "other", 13 | "otp", 14 | "pin", 15 | "PWS", 16 | "skip", 17 | "skip_by_default", 18 | "slowtest", 19 | "status", 20 | "unencrypted", 21 | "update", 22 | ] -------------------------------------------------------------------------------- /unittest/requirements.txt: -------------------------------------------------------------------------------- 1 | cffi 2 | pytest 3 | pytest-repeat 4 | pytest-randomly 5 | tqdm 6 | oath 7 | hypothesis 8 | -------------------------------------------------------------------------------- /unittest/setup_python_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pip install -r requirements.txt --user 4 | -------------------------------------------------------------------------------- /unittest/test1.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "catch2/catch.hpp" 23 | 24 | #include 25 | #include 26 | #include "device_proto.h" 27 | #include "log.h" 28 | #include "stick10_commands.h" 29 | 30 | using namespace std; 31 | using namespace nitrokey::device; 32 | using namespace nitrokey::proto::stick10; 33 | using namespace nitrokey::log; 34 | using namespace nitrokey::misc; 35 | 36 | using Dev10 = std::shared_ptr; 37 | 38 | std::string getSlotName(Dev10 stick, int slotNo) { 39 | auto slot_req = get_payload(); 40 | slot_req.slot_number = slotNo; 41 | auto slot = ReadSlot::CommandTransaction::run(stick, slot_req); 42 | std::string sName(reinterpret_cast(slot.data().slot_name)); 43 | return sName; 44 | } 45 | 46 | TEST_CASE("Slot names are correct", "[slotNames]") { 47 | auto stick = make_shared(); 48 | bool connected = stick->connect(); 49 | REQUIRE(connected == true); 50 | 51 | Log::instance().set_loglevel(Loglevel::DEBUG); 52 | 53 | auto resp = GetStatus::CommandTransaction::run(stick); 54 | 55 | auto authreq = get_payload(); 56 | strcpy((char *)(authreq.card_password), "12345678"); 57 | FirstAuthenticate::CommandTransaction::run(stick, authreq); 58 | 59 | { 60 | auto authreq = get_payload(); 61 | strcpy((char *)(authreq.user_password), "123456"); 62 | EnablePasswordSafe::CommandTransaction::run(stick, authreq); 63 | } 64 | 65 | //assuming these values were set earlier, thus failing on normal use 66 | REQUIRE(getSlotName(stick, 0x20) == std::string("1")); 67 | REQUIRE(getSlotName(stick, 0x21) == std::string("slot2")); 68 | 69 | { 70 | auto resp = GetPasswordRetryCount::CommandTransaction::run(stick); 71 | REQUIRE(resp.data().password_retry_count == 3); 72 | } 73 | { 74 | auto resp = GetUserPasswordRetryCount::CommandTransaction::run(stick); 75 | REQUIRE(resp.data().password_retry_count == 3); 76 | } 77 | 78 | { 79 | auto slot = get_payload(); 80 | slot.slot_number = 0; 81 | auto resp2 = GetPasswordSafeSlotName::CommandTransaction::run(stick, slot); 82 | std::string sName(reinterpret_cast(resp2.data().slot_name)); 83 | REQUIRE(sName == std::string("web1")); 84 | } 85 | 86 | { 87 | auto slot = get_payload(); 88 | slot.slot_number = 0; 89 | auto resp2 = 90 | GetPasswordSafeSlotPassword::CommandTransaction::run(stick, slot); 91 | std::string sName(reinterpret_cast(resp2.data().slot_password)); 92 | REQUIRE(sName == std::string("pass1")); 93 | } 94 | 95 | { 96 | auto slot = get_payload(); 97 | slot.slot_number = 0; 98 | auto resp2 = GetPasswordSafeSlotLogin::CommandTransaction::run(stick, slot); 99 | std::string sName(reinterpret_cast(resp2.data().slot_login)); 100 | REQUIRE(sName == std::string("login1")); 101 | } 102 | 103 | stick->disconnect(); 104 | } 105 | -------------------------------------------------------------------------------- /unittest/test3.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | 23 | static const char *const default_admin_pin = "12345678"; 24 | static const char *const default_user_pin = "123456"; 25 | const char * temporary_password = "123456789012345678901234"; 26 | const char * RFC_SECRET = "12345678901234567890"; 27 | 28 | #include "catch2/catch.hpp" 29 | 30 | #include 31 | #include 32 | #include 33 | #include "device_proto.h" 34 | #include "log.h" 35 | #include "stick10_commands_0.8.h" 36 | //#include "stick20_commands.h" 37 | 38 | using namespace std; 39 | using namespace nitrokey::device; 40 | using namespace nitrokey::proto; 41 | using namespace nitrokey::proto::stick10_08; 42 | using namespace nitrokey::log; 43 | using namespace nitrokey::misc; 44 | 45 | using Dev = Stick10; 46 | using Dev10 = std::shared_ptr; 47 | 48 | void connect_and_setup(Dev10 stick) { 49 | bool connected = stick->connect(); 50 | REQUIRE(connected == true); 51 | Log::instance().set_loglevel(Loglevel::DEBUG); 52 | } 53 | 54 | void authorize(Dev10 stick) { 55 | auto authreq = get_payload(); 56 | strcpy((char *) (authreq.card_password), default_admin_pin); 57 | strcpy((char *) (authreq.temporary_password), temporary_password); 58 | FirstAuthenticate::CommandTransaction::run(stick, authreq); 59 | 60 | auto user_auth = get_payload(); 61 | strcpyT(user_auth.temporary_password, temporary_password); 62 | strcpyT(user_auth.card_password, default_user_pin); 63 | UserAuthenticate::CommandTransaction::run(stick, user_auth); 64 | } 65 | 66 | TEST_CASE("write slot", "[pronew]"){ 67 | auto stick = make_shared(); 68 | 69 | connect_and_setup(stick); 70 | authorize(stick); 71 | 72 | auto p2 = get_payload(); 73 | strcpyT(p2.temporary_admin_password, temporary_password); 74 | p2.setTypeName(); 75 | strcpyT(p2.data, "test name aaa"); 76 | stick10_08::SendOTPData::CommandTransaction::run(stick, p2); 77 | 78 | p2 = get_payload(); 79 | strcpyT(p2.temporary_admin_password, temporary_password); 80 | strcpyT(p2.data, RFC_SECRET); 81 | p2.setTypeSecret(); 82 | stick10_08::SendOTPData::CommandTransaction::run(stick, p2); 83 | 84 | auto p = get_payload(); 85 | strcpyT(p.temporary_admin_password, temporary_password); 86 | p.use_8_digits = true; 87 | p.slot_number = 0 + 0x10; 88 | p.slot_counter_or_interval = 0; 89 | stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p); 90 | 91 | auto pc = get_payload(); 92 | pc.enable_user_password = 0; 93 | strcpyT(pc.temporary_admin_password, temporary_password); 94 | WriteGeneralConfig::CommandTransaction::run(stick, pc); 95 | 96 | auto p3 = get_payload(); 97 | p3.slot_number = 0 + 0x10; 98 | GetHOTP::CommandTransaction::run(stick, p3); 99 | 100 | } 101 | 102 | 103 | TEST_CASE("erase slot", "[pronew]"){ 104 | auto stick = make_shared(); 105 | connect_and_setup(stick); 106 | authorize(stick); 107 | 108 | auto p = get_payload(); 109 | p.enable_user_password = 0; 110 | strcpyT(p.temporary_admin_password, temporary_password); 111 | WriteGeneralConfig::CommandTransaction::run(stick, p); 112 | 113 | auto p3 = get_payload(); 114 | p3.slot_number = 0 + 0x10; 115 | GetHOTP::CommandTransaction::run(stick, p3); 116 | 117 | auto erase_payload = get_payload(); 118 | erase_payload.slot_number = 0 + 0x10; 119 | strcpyT(erase_payload.temporary_admin_password, temporary_password); 120 | EraseSlot::CommandTransaction::run(stick, erase_payload); 121 | 122 | auto p4 = get_payload(); 123 | p4.slot_number = 0 + 0x10; 124 | REQUIRE_THROWS( 125 | GetHOTP::CommandTransaction::run(stick, p4) 126 | ); 127 | } 128 | 129 | TEST_CASE("write general config", "[pronew]") { 130 | auto stick = make_shared(); 131 | connect_and_setup(stick); 132 | authorize(stick); 133 | 134 | auto p = get_payload(); 135 | p.enable_user_password = 1; 136 | REQUIRE_THROWS( 137 | WriteGeneralConfig::CommandTransaction::run(stick, p) 138 | ); 139 | strcpyT(p.temporary_admin_password, temporary_password); 140 | WriteGeneralConfig::CommandTransaction::run(stick, p); 141 | } 142 | 143 | TEST_CASE("authorize user HOTP", "[pronew]") { 144 | auto stick = make_shared(); 145 | connect_and_setup(stick); 146 | authorize(stick); 147 | 148 | { 149 | auto p = get_payload(); 150 | p.enable_user_password = 1; 151 | strcpyT(p.temporary_admin_password, temporary_password); 152 | WriteGeneralConfig::CommandTransaction::run(stick, p); 153 | } 154 | 155 | auto p2 = get_payload(); 156 | strcpyT(p2.temporary_admin_password, temporary_password); 157 | p2.setTypeName(); 158 | strcpyT(p2.data, "test name aaa"); 159 | stick10_08::SendOTPData::CommandTransaction::run(stick, p2); 160 | 161 | p2 = get_payload(); 162 | strcpyT(p2.temporary_admin_password, temporary_password); 163 | strcpyT(p2.data, RFC_SECRET); 164 | p2.setTypeSecret(); 165 | stick10_08::SendOTPData::CommandTransaction::run(stick, p2); 166 | 167 | auto p = get_payload(); 168 | strcpyT(p.temporary_admin_password, temporary_password); 169 | p.use_8_digits = true; 170 | p.slot_number = 0 + 0x10; 171 | p.slot_counter_or_interval = 0; 172 | stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p); 173 | 174 | 175 | auto p3 = get_payload(); 176 | p3.slot_number = 0 + 0x10; 177 | REQUIRE_THROWS( 178 | GetHOTP::CommandTransaction::run(stick, p3) 179 | ); 180 | strcpyT(p3.temporary_user_password, temporary_password); 181 | auto code_response = GetHOTP::CommandTransaction::run(stick, p3); 182 | REQUIRE(code_response.data().code == 84755224); 183 | 184 | } 185 | 186 | TEST_CASE("check firmware version", "[pronew]") { 187 | auto stick = make_shared(); 188 | connect_and_setup(stick); 189 | 190 | auto p = GetStatus::CommandTransaction::run(stick); 191 | REQUIRE(p.data().firmware_version == 8); 192 | } 193 | 194 | TEST_CASE("authorize user TOTP", "[pronew]") { 195 | auto stick = make_shared(); 196 | connect_and_setup(stick); 197 | authorize(stick); 198 | 199 | { 200 | auto p = get_payload(); 201 | p.enable_user_password = 1; 202 | strcpyT(p.temporary_admin_password, temporary_password); 203 | WriteGeneralConfig::CommandTransaction::run(stick, p); 204 | } 205 | 206 | auto p2 = get_payload(); 207 | strcpyT(p2.temporary_admin_password, temporary_password); 208 | p2.setTypeName(); 209 | strcpyT(p2.data, "test name TOTP"); 210 | stick10_08::SendOTPData::CommandTransaction::run(stick, p2); 211 | 212 | p2 = get_payload(); 213 | strcpyT(p2.temporary_admin_password, temporary_password); 214 | strcpyT(p2.data, RFC_SECRET); 215 | p2.setTypeSecret(); 216 | stick10_08::SendOTPData::CommandTransaction::run(stick, p2); 217 | 218 | auto p = get_payload(); 219 | strcpyT(p.temporary_admin_password, temporary_password); 220 | p.use_8_digits = true; 221 | p.slot_number = 0 + 0x20; 222 | p.slot_counter_or_interval = 30; 223 | stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p); 224 | 225 | auto p_get_totp = get_payload(); 226 | p_get_totp.slot_number = 0 + 0x20; 227 | 228 | REQUIRE_THROWS( 229 | GetTOTP::CommandTransaction::run(stick, p_get_totp) 230 | ); 231 | strcpyT(p_get_totp.temporary_user_password, temporary_password); 232 | 233 | auto p_set_time = get_payload(); 234 | p_set_time.reset = 1; 235 | p_set_time.time = 59; 236 | SetTime::CommandTransaction::run(stick, p_set_time); 237 | auto code = GetTOTP::CommandTransaction::run(stick, p_get_totp); 238 | REQUIRE(code.data().code == 94287082); 239 | 240 | } 241 | -------------------------------------------------------------------------------- /unittest/test_C_API.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | static const int TOO_LONG_STRING = 200; 23 | 24 | #include "catch2/catch.hpp" 25 | 26 | #include 27 | #include 28 | #include "log.h" 29 | #include "../NK_C_API.h" 30 | 31 | int login; 32 | 33 | TEST_CASE("C API connect", "[BASIC]") { 34 | login = NK_login_auto(); 35 | REQUIRE(login != 0); 36 | NK_logout(); 37 | login = NK_login_auto(); 38 | REQUIRE(login != 0); 39 | NK_logout(); 40 | login = NK_login_auto(); 41 | REQUIRE(login != 0); 42 | } 43 | 44 | TEST_CASE("Check retry count", "[BASIC]") { 45 | REQUIRE(login != 0); 46 | NK_set_debug_level(3); 47 | REQUIRE(NK_get_admin_retry_count() == 3); 48 | REQUIRE(NK_get_user_retry_count() == 3); 49 | } 50 | 51 | TEST_CASE("Check long strings", "[STANDARD]") { 52 | REQUIRE(login != 0); 53 | const char* longPin = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 54 | const char* pin = "123123123"; 55 | auto result = NK_change_user_PIN(longPin, pin); 56 | REQUIRE(result == TOO_LONG_STRING); 57 | result = NK_change_user_PIN(pin, longPin); 58 | REQUIRE(result == TOO_LONG_STRING); 59 | CAPTURE(result); 60 | } 61 | 62 | #include 63 | 64 | TEST_CASE("multiple devices with ID", "[BASIC]") { 65 | NK_logout(); 66 | NK_set_debug_level(3); 67 | auto s = NK_list_devices_by_cpuID(); 68 | REQUIRE(s!=nullptr); 69 | REQUIRE(strnlen(s, 4096) < 4096); 70 | REQUIRE(strnlen(s, 4096) > 2*4); 71 | std::cout << s << std::endl; 72 | 73 | char *string, *token; 74 | int t; 75 | 76 | string = strndup(s, 4096); 77 | free (static_cast(const_cast(s))); 78 | 79 | while ((token = strsep(&string, ";")) != nullptr){ 80 | if (strnlen(token, 4096) < 3) continue; 81 | std::cout << token << " connecting: "; 82 | std::cout << (t=NK_connect_with_ID(token)) << std::endl; 83 | REQUIRE(t == 1); 84 | } 85 | 86 | free (string); 87 | } 88 | 89 | TEST_CASE("Get device model", "[BASIC]") { 90 | NK_logout(); 91 | NK_device_model model = NK_get_device_model(); 92 | REQUIRE(model == NK_device_model::NK_DISCONNECTED); 93 | 94 | auto success = NK_login_auto() == 1; 95 | REQUIRE(success); 96 | model = NK_get_device_model(); 97 | REQUIRE(model != NK_device_model::NK_DISCONNECTED); 98 | 99 | REQUIRE((model == NK_PRO || model == NK_STORAGE)); 100 | NK_logout(); 101 | } 102 | -------------------------------------------------------------------------------- /unittest/test_HOTP.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | 23 | #include "catch2/catch.hpp" 24 | #include 25 | #include "device_proto.h" 26 | #include "log.h" 27 | #include "stick10_commands.h" 28 | #include 29 | #include "misc.h" 30 | 31 | using namespace std; 32 | using namespace nitrokey::device; 33 | using namespace nitrokey::proto::stick10; 34 | using namespace nitrokey::log; 35 | using namespace nitrokey::misc; 36 | 37 | void hexStringToByte(uint8_t data[], const char* hexString){ 38 | REQUIRE(strlen(hexString)%2==0); 39 | char buf[3]; 40 | buf[2] = '\0'; 41 | for(size_t i=0; i(slot_secret) ) == 0 ); 56 | } 57 | 58 | TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") { 59 | std::shared_ptr stick = make_shared(); 60 | bool connected = stick->connect(); 61 | 62 | REQUIRE(connected == true); 63 | 64 | Log::instance().set_loglevel(Loglevel::DEBUG); 65 | 66 | auto resp = GetStatus::CommandTransaction::run(stick); 67 | 68 | const char * temporary_password = "123456789012345678901234"; 69 | { 70 | auto authreq = get_payload(); 71 | strcpy((char *)(authreq.card_password), "12345678"); 72 | strcpy((char *)(authreq.temporary_password), temporary_password); 73 | FirstAuthenticate::CommandTransaction::run(stick, authreq); 74 | } 75 | 76 | //test according to https://tools.ietf.org/html/rfc4226#page-32 77 | { 78 | auto hwrite = get_payload(); 79 | hwrite.slot_number = 0x10; 80 | strcpy(reinterpret_cast(hwrite.slot_name), "rfc4226_lib"); 81 | //strcpy(reinterpret_cast(hwrite.slot_secret), ""); 82 | const char* secretHex = "3132333435363738393031323334353637383930"; 83 | hexStringToByte(hwrite.slot_secret, secretHex); 84 | 85 | //hwrite.slot_config; //TODO check various configs in separate test cases 86 | //strcpy(reinterpret_cast(hwrite.slot_token_id), ""); 87 | //strcpy(reinterpret_cast(hwrite.slot_counter), ""); 88 | 89 | //authorize writehotp first 90 | { 91 | auto auth = get_payload(); 92 | strcpy((char *)(auth.temporary_password), temporary_password); 93 | auth.crc_to_authorize = WriteToHOTPSlot::CommandTransaction::getCRC(hwrite); 94 | Authorize::CommandTransaction::run(stick, auth); 95 | } 96 | 97 | //run hotp command 98 | WriteToHOTPSlot::CommandTransaction::run(stick, hwrite); 99 | 100 | uint32_t codes[] = { 101 | 755224, 287082, 359152, 969429, 338314, 102 | 254676, 287922, 162583, 399871, 520489 103 | }; 104 | 105 | for( auto code: codes){ 106 | auto gh = get_payload(); 107 | gh.slot_number = 0x10; 108 | auto resp = GetHOTP::CommandTransaction::run(stick, gh); 109 | REQUIRE( resp.data().code == code); 110 | } 111 | //checking slot programmed before with nitro-app 112 | /* 113 | for( auto code: codes){ 114 | GetHOTP::CommandTransaction::CommandPayload gh; 115 | gh.slot_number = 0x12; 116 | auto resp = GetHOTP::CommandTransaction::run(stick, gh); 117 | REQUIRE( resp.code == code); 118 | } 119 | */ 120 | } 121 | 122 | 123 | stick->disconnect(); 124 | } 125 | -------------------------------------------------------------------------------- /unittest/test_command_ids_header.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #ifndef LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H 23 | #define LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H 24 | 25 | #define STICK20_CMD_START_VALUE 0x20 26 | #define STICK20_CMD_ENABLE_CRYPTED_PARI (STICK20_CMD_START_VALUE + 0) 27 | #define STICK20_CMD_DISABLE_CRYPTED_PARI (STICK20_CMD_START_VALUE + 1) 28 | #define STICK20_CMD_ENABLE_HIDDEN_CRYPTED_PARI (STICK20_CMD_START_VALUE + 2) 29 | #define STICK20_CMD_DISABLE_HIDDEN_CRYPTED_PARI (STICK20_CMD_START_VALUE + 3) 30 | #define STICK20_CMD_ENABLE_FIRMWARE_UPDATE (STICK20_CMD_START_VALUE + 4) 31 | #define STICK20_CMD_EXPORT_FIRMWARE_TO_FILE (STICK20_CMD_START_VALUE + 5) 32 | #define STICK20_CMD_GENERATE_NEW_KEYS (STICK20_CMD_START_VALUE + 6) 33 | #define STICK20_CMD_FILL_SD_CARD_WITH_RANDOM_CHARS (STICK20_CMD_START_VALUE + 7) 34 | 35 | #define STICK20_CMD_WRITE_STATUS_DATA (STICK20_CMD_START_VALUE + 8) 36 | #define STICK20_CMD_ENABLE_READONLY_UNCRYPTED_LUN (STICK20_CMD_START_VALUE + 9) 37 | #define STICK20_CMD_ENABLE_READWRITE_UNCRYPTED_LUN (STICK20_CMD_START_VALUE + 10) 38 | 39 | #define STICK20_CMD_SEND_PASSWORD_MATRIX (STICK20_CMD_START_VALUE + 11) 40 | #define STICK20_CMD_SEND_PASSWORD_MATRIX_PINDATA (STICK20_CMD_START_VALUE + 12) 41 | #define STICK20_CMD_SEND_PASSWORD_MATRIX_SETUP (STICK20_CMD_START_VALUE + 13) 42 | 43 | #define STICK20_CMD_GET_DEVICE_STATUS (STICK20_CMD_START_VALUE + 14) 44 | #define STICK20_CMD_SEND_DEVICE_STATUS (STICK20_CMD_START_VALUE + 15) 45 | 46 | #define STICK20_CMD_SEND_HIDDEN_VOLUME_PASSWORD (STICK20_CMD_START_VALUE + 16) 47 | #define STICK20_CMD_SEND_HIDDEN_VOLUME_SETUP (STICK20_CMD_START_VALUE + 17) 48 | #define STICK20_CMD_SEND_PASSWORD (STICK20_CMD_START_VALUE + 18) 49 | #define STICK20_CMD_SEND_NEW_PASSWORD (STICK20_CMD_START_VALUE + 19) 50 | #define STICK20_CMD_CLEAR_NEW_SD_CARD_FOUND (STICK20_CMD_START_VALUE + 20) 51 | 52 | #define STICK20_CMD_SEND_STARTUP (STICK20_CMD_START_VALUE + 21) 53 | #define STICK20_CMD_SEND_CLEAR_STICK_KEYS_NOT_INITIATED (STICK20_CMD_START_VALUE + 22) 54 | #define STICK20_CMD_SEND_LOCK_STICK_HARDWARE (STICK20_CMD_START_VALUE + 23) 55 | 56 | #define STICK20_CMD_PRODUCTION_TEST (STICK20_CMD_START_VALUE + 24) 57 | #define STICK20_CMD_SEND_DEBUG_DATA (STICK20_CMD_START_VALUE + 25) 58 | 59 | #define STICK20_CMD_CHANGE_UPDATE_PIN (STICK20_CMD_START_VALUE + 26) 60 | 61 | 62 | #endif //LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H 63 | -------------------------------------------------------------------------------- /unittest/test_issues.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | 23 | const char * const default_admin_pin = "12345678"; 24 | const char * const default_user_pin = "123456"; 25 | const char * const temporary_password = "123456789012345678901234"; 26 | const char * const RFC_SECRET = "12345678901234567890"; 27 | const char * const hidden_volume_pass = "123456789012345"; 28 | 29 | #include "catch2/catch.hpp" 30 | 31 | #include 32 | 33 | using namespace std; 34 | using namespace nitrokey; 35 | 36 | 37 | bool test_36(){ 38 | auto i = NitrokeyManager::instance(); 39 | i->set_loglevel(3); 40 | REQUIRE(i->connect()); 41 | 42 | for (int j = 0; j < 200; ++j) { 43 | i->get_status(); 44 | i->get_status_storage_as_string(); 45 | INFO( "Iteration: " << j); 46 | } 47 | return true; 48 | } 49 | 50 | bool test_31(){ 51 | auto i = NitrokeyManager::instance(); 52 | i->set_loglevel(3); 53 | REQUIRE(i->connect()); 54 | 55 | // i->unlock_encrypted_volume(default_user_pin); 56 | // i->create_hidden_volume(0, 70, 80, hidden_volume_pass); 57 | // i->lock_device(); 58 | 59 | try{ 60 | i->get_password_safe_slot_status(); 61 | } 62 | catch (...){ 63 | //pass 64 | } 65 | 66 | i->get_status_storage(); 67 | i->get_admin_retry_count(); 68 | i->get_status_storage(); 69 | i->get_user_retry_count(); 70 | i->unlock_encrypted_volume(default_user_pin); 71 | i->get_status_storage(); 72 | i->get_password_safe_slot_status(); 73 | i->get_status_storage(); 74 | i->get_user_retry_count(); 75 | i->get_password_safe_slot_status(); 76 | i->get_status(); 77 | i->get_status_storage(); 78 | i->get_admin_retry_count(); 79 | i->get_status(); 80 | i->get_user_retry_count(); 81 | i->unlock_hidden_volume(hidden_volume_pass); 82 | i->get_status_storage(); 83 | i->get_password_safe_slot_status(); 84 | 85 | 86 | return true; 87 | } 88 | 89 | TEST_CASE("issue 31", "[issue]"){ 90 | { 91 | auto i = NitrokeyManager::instance(); 92 | i->set_loglevel(4); 93 | REQUIRE(i->connect()); 94 | 95 | i->unlock_encrypted_volume(default_user_pin); 96 | i->create_hidden_volume(0, 70, 80, hidden_volume_pass); 97 | i->lock_device(); 98 | } 99 | 100 | for(int i=0; i<20; i++){ 101 | REQUIRE(test_31()); 102 | } 103 | } 104 | 105 | 106 | 107 | TEST_CASE("issue 36", "[issue]"){ 108 | REQUIRE(test_36()); 109 | } 110 | -------------------------------------------------------------------------------- /unittest/test_issues.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | import pytest 4 | 5 | from conftest import skip_if_device_version_lower_than 6 | from constants import DefaultPasswords, DeviceErrorCode 7 | from misc import gs, ffi 8 | from test_pro import check_HOTP_RFC_codes, test_random 9 | 10 | 11 | def test_destroy_encrypted_data_leaves_OTP_intact(C): 12 | """ 13 | Make sure AES key regeneration will not remove OTP records. 14 | Test for Nitrokey Storage. 15 | Details: https://github.com/Nitrokey/libnitrokey/issues/199 16 | """ 17 | skip_if_device_version_lower_than({'S': 55}) 18 | 19 | assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK 20 | # write password safe slot 21 | assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK 22 | # read slot 23 | assert gs(C.NK_get_password_safe_slot_name(0)) == b'slotname1' 24 | assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK 25 | slot_login = C.NK_get_password_safe_slot_login(0) 26 | assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK 27 | assert gs(slot_login) == b'login1' 28 | 29 | # write OTP 30 | use_8_digits = False 31 | use_pin_protection = False 32 | assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 33 | assert C.NK_write_config(255, 255, 255, use_pin_protection, not use_pin_protection, 34 | DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 35 | check_HOTP_RFC_codes(C, lambda x: gs(C.NK_get_hotp_code(x)), use_8_digits=use_8_digits) 36 | 37 | # confirm OTP 38 | assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 39 | assert gs(C.NK_get_hotp_slot_name(1)) == b'python_test' 40 | 41 | # destroy password safe by regenerating aes key 42 | assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK 43 | 44 | assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 45 | assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK 46 | assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK 47 | assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK 48 | assert gs(C.NK_get_password_safe_slot_name(0)) != b'slotname1' 49 | assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK 50 | 51 | # confirm OTP 52 | assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 53 | assert gs(C.NK_get_hotp_slot_name(1)) == b'python_test' 54 | 55 | 56 | class Modes(Enum): 57 | EmptyBody = 0 58 | FactoryResetWithAES = 1 59 | FactoryReset = 2 60 | AESGeneration = 3 61 | 62 | @pytest.mark.firmware 63 | @pytest.mark.factory_reset 64 | @pytest.mark.parametrize("mode", map(Modes, reversed(range(4)))) 65 | def test_pro_factory_reset_breaks_update_password(C, mode: Modes): 66 | from test_pro_bootloader import test_bootloader_password_change_pro, test_bootloader_password_change_pro_length 67 | from test_pro import test_factory_reset 68 | skip_if_device_version_lower_than({'P': 14}) 69 | 70 | func = { 71 | Modes.EmptyBody: lambda: True, 72 | Modes.FactoryResetWithAES: lambda: test_factory_reset(C) or True, 73 | Modes.FactoryReset: lambda: C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK, 74 | Modes.AESGeneration: lambda: C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK, 75 | } 76 | 77 | def boot_test(C): 78 | test_bootloader_password_change_pro(C) 79 | # test_bootloader_password_change_pro_length(C) 80 | 81 | def random(C): 82 | data = ffi.new('struct GetRandom_t *') 83 | req_count = 50 84 | res = C.NK_get_random(req_count, data) 85 | assert res == DeviceErrorCode.STATUS_OK 86 | assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK 87 | assert data.op_success == 1 88 | assert data.size_effective == req_count 89 | 90 | random(C) 91 | boot_test(C) 92 | random(C) 93 | func[mode]() 94 | random(C) # fails here 95 | boot_test(C) 96 | random(C) 97 | -------------------------------------------------------------------------------- /unittest/test_library.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2015-2018 Nitrokey UG 3 | 4 | This file is part of libnitrokey. 5 | 6 | libnitrokey is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | libnitrokey is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with libnitrokey. If not, see . 18 | 19 | SPDX-License-Identifier: LGPL-3.0 20 | """ 21 | 22 | import pytest 23 | 24 | from misc import ffi, gs, to_hex, is_pro_rtm_07, is_long_OTP_secret_handled 25 | from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors, bbRFC_SECRET 26 | 27 | 28 | def test_too_long_strings(C): 29 | new_password = b'123123123' 30 | long_string = b'a' * 100 31 | assert C.NK_change_user_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING 32 | assert C.NK_change_user_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING 33 | assert C.NK_change_admin_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING 34 | assert C.NK_change_admin_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING 35 | assert C.NK_first_authenticate(long_string, DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING 36 | assert C.NK_erase_totp_slot(0, long_string) == LibraryErrors.TOO_LONG_STRING 37 | digits = False 38 | assert C.NK_write_hotp_slot(1, long_string, bbRFC_SECRET, 0, digits, False, False, b"", 39 | DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING 40 | assert C.NK_write_hotp_slot(1, b'long_test', bbRFC_SECRET, 0, digits, False, False, b"", 41 | long_string) == LibraryErrors.TOO_LONG_STRING 42 | assert gs(C.NK_get_hotp_code_PIN(0, long_string)) == b"" 43 | assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING 44 | assert C.NK_change_firmware_password_pro(long_string, long_string) == LibraryErrors.TOO_LONG_STRING 45 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING 46 | 47 | 48 | def test_invalid_slot(C): 49 | invalid_slot = 255 50 | assert C.NK_erase_totp_slot(invalid_slot, b'some password') == LibraryErrors.INVALID_SLOT 51 | assert C.NK_write_hotp_slot(invalid_slot, b'long_test', bbRFC_SECRET, 0, False, False, False, b"", 52 | b'aaa') == LibraryErrors.INVALID_SLOT 53 | assert gs(C.NK_get_hotp_code_PIN(invalid_slot, b'some password')) == b"" 54 | assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT 55 | assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT 56 | assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK 57 | assert gs(C.NK_get_password_safe_slot_name(invalid_slot)) == b'' 58 | assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT 59 | assert gs(C.NK_get_password_safe_slot_login(invalid_slot)) == b'' 60 | assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT 61 | 62 | 63 | @pytest.mark.parametrize("invalid_hex_string", 64 | ['text', '00 ', '0xff', 'zzzzzzzzzzzz', 'fff', 'f' * 257, 'f' * 258]) 65 | def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string): 66 | """ 67 | Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number, 68 | empty or longer than 255 characters. 69 | """ 70 | invalid_hex_string = invalid_hex_string.encode('ascii') 71 | assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK 72 | assert C.NK_write_hotp_slot(1, b'slot_name', invalid_hex_string, 0, True, False, False, b'', 73 | DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING 74 | assert C.NK_write_totp_slot(1, b'python_test', invalid_hex_string, 30, True, False, False, b"", 75 | DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING 76 | 77 | def test_warning_binary_bigger_than_secret_buffer(C): 78 | invalid_hex_string = to_hex('1234567890') * 3 79 | invalid_hex_string = invalid_hex_string.encode('ascii') 80 | if is_long_OTP_secret_handled(C): 81 | invalid_hex_string *= 2 82 | assert C.NK_write_hotp_slot(1, b'slot_name', invalid_hex_string, 0, True, False, False, b'', 83 | DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE 84 | 85 | 86 | @pytest.mark.skip(reason='Experimental') 87 | def test_clear(C): 88 | d = 'asdasdasd' 89 | print(d) 90 | C.clear_password(d) 91 | print(d) -------------------------------------------------------------------------------- /unittest/test_memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include 23 | #include "../NK_C_API.h" 24 | 25 | // This test should be run with valgrind to make sure that there are no 26 | // memory leaks in the tested functions: 27 | // valgrind ./test_memory 28 | int main() { 29 | int result = NK_login_auto(); 30 | if (result != 1) 31 | return 1; 32 | 33 | int retry_count = NK_get_admin_retry_count(); 34 | if (retry_count != 3) 35 | return 1; 36 | retry_count = NK_get_user_retry_count(); 37 | if (retry_count != 3) 38 | return 1; 39 | 40 | enum NK_device_model model = NK_get_device_model(); 41 | if (model != NK_PRO && model != NK_STORAGE) 42 | return 1; 43 | 44 | uint8_t *config = NK_read_config(); 45 | if (config == NULL) 46 | return 1; 47 | NK_free_config(config); 48 | 49 | result = NK_enable_password_safe("123456"); 50 | if (result != 0) 51 | return 1; 52 | 53 | uint8_t *slot_status = NK_get_password_safe_slot_status(); 54 | if (slot_status == NULL) { 55 | return 1; 56 | } 57 | NK_free_password_safe_slot_status(slot_status); 58 | 59 | NK_logout(); 60 | 61 | return 0; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /unittest/test_minimal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "../NK_C_API.h" 23 | 24 | // This test is only intended to make sure that the C API header can be 25 | // compiled by a C compiler. (All other tests are written in C++.) 26 | int main() { 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /unittest/test_multiple.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2017-2018 Nitrokey UG 3 | 4 | This file is part of libnitrokey. 5 | 6 | libnitrokey is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | libnitrokey is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with libnitrokey. If not, see . 18 | 19 | SPDX-License-Identifier: LGPL-3.0 20 | """ 21 | 22 | import pprint 23 | from time import sleep 24 | 25 | import pytest 26 | from collections import defaultdict 27 | 28 | from tqdm import tqdm 29 | 30 | from conftest import skip_if_device_version_lower_than 31 | from constants import DefaultPasswords, DeviceErrorCode 32 | from misc import gs, wait, ffi, bb 33 | 34 | pprint = pprint.PrettyPrinter(indent=4).pprint 35 | 36 | 37 | @pytest.mark.other 38 | @pytest.mark.info 39 | def test_list_devices(C): 40 | infos = C.NK_list_devices() 41 | assert infos != ffi.NULL 42 | C.NK_free_device_info(infos) 43 | 44 | 45 | @pytest.mark.other 46 | @pytest.mark.info 47 | def test_connect_with_path(C): 48 | ids = gs(C.NK_list_devices_by_cpuID()) 49 | # NK_list_devices_by_cpuID already opened the devices, so we have to close 50 | # them before trying to reconnect 51 | assert C.NK_logout() == 0 52 | 53 | devices_list = ids.split(b';') 54 | for value in devices_list: 55 | parts = value.split(b'_p_') 56 | assert len(parts) < 3 57 | if len(parts) == 2: 58 | path = parts[1] 59 | else: 60 | path = parts[0] 61 | assert C.NK_connect_with_path(path) == 1 62 | 63 | 64 | @pytest.mark.other 65 | @pytest.mark.info 66 | def test_get_status_storage_multiple(C): 67 | ids = gs(C.NK_list_devices_by_cpuID()) 68 | print(ids) 69 | devices_list = ids.split(b';') 70 | # 71 | # for s in devices_list: 72 | # res = C.NK_connect_with_ID(s) 73 | # assert res == 1 74 | # # st = gs(C.NK_get_status_storage_as_string()) 75 | # # print(len(st)) 76 | # C.NK_lock_device() 77 | # 78 | for s in devices_list: 79 | res = C.NK_connect_with_ID(s) 80 | assert res == 1 81 | v = C.NK_fill_SD_card_with_random_data(b'12345678') 82 | # print(v) 83 | 84 | devices_count = len(devices_list) 85 | assert devices_count != 0 86 | b = 0 87 | 88 | last_b = 0 89 | with tqdm(total=devices_count*100) as pbar: 90 | while b/devices_count < 100: 91 | pbar.update(b - last_b) 92 | 93 | b = defaultdict (lambda: 0) 94 | 95 | ids = gs(C.NK_list_devices_by_cpuID()) 96 | devices_list = ids.split(b';') 97 | devices_count = len(devices_list) 98 | 99 | for s in devices_list: 100 | res = C.NK_connect_with_ID(s) 101 | if res != 1: continue 102 | b[s] += C.NK_get_progress_bar_value() 103 | print(b) 104 | b = sum(b.values()) 105 | print('{}: {}'.format(b, int(b/devices_count) * '=')) 106 | sleep(5) 107 | 108 | 109 | -------------------------------------------------------------------------------- /unittest/test_multiple_devices.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | static const char *const default_admin_pin = "12345678"; 23 | static const char *const default_user_pin = "123456"; 24 | const char * temporary_password = "123456789012345678901234"; 25 | const char * RFC_SECRET = "12345678901234567890"; 26 | 27 | #include "catch2/catch.hpp" 28 | 29 | #include 30 | #include 31 | #include 32 | #include "../NK_C_API.h" 33 | 34 | using namespace nitrokey; 35 | 36 | 37 | TEST_CASE("List devices", "[BASIC]") { 38 | auto v = Device::enumerate(); 39 | REQUIRE(v.size() > 0); 40 | for (auto i : v){ 41 | auto d = Device::create(i.m_deviceModel); 42 | if (!d) { 43 | std::cout << "Could not create device with model " << i.m_deviceModel << "\n"; 44 | continue; 45 | } 46 | std::cout << i.m_deviceModel << " " << i.m_path << " " 47 | << i.m_serialNumber << " |"; 48 | d->set_path(i.m_path); 49 | d->connect(); 50 | auto res = GetStatus::CommandTransaction::run(d); 51 | std::cout << " " << res.data().card_serial_u32 << " " 52 | << res.data().get_card_serial_hex() 53 | << std::endl; 54 | d->disconnect(); 55 | } 56 | } 57 | 58 | TEST_CASE("List Storage devices", "[BASIC]") { 59 | shared_ptr d = make_shared(); 60 | auto v = Device::enumerate(); 61 | REQUIRE(v.size() > 0); 62 | for (auto i : v){ 63 | if (i.m_deviceModel != DeviceModel::STORAGE) 64 | continue; 65 | auto a = i.m_path; 66 | std::cout << a; 67 | d->set_path(a); 68 | d->connect(); 69 | auto res = GetStatus::CommandTransaction::run(d); 70 | auto res2 = GetDeviceStatus::CommandTransaction::run(d); 71 | std::cout << " " << res.data().card_serial_u32 << " " 72 | << res.data().get_card_serial_hex() 73 | << " " << std::to_string(res2.data().versionInfo.minor) 74 | << std::endl; 75 | d->disconnect(); 76 | } 77 | } 78 | 79 | TEST_CASE("Regenerate AES keys", "[BASIC]") { 80 | shared_ptr d = make_shared(); 81 | auto v = Device::enumerate(); 82 | REQUIRE(v.size() > 0); 83 | 84 | std::vector> devices; 85 | for (auto i : v){ 86 | if (i.m_deviceModel != DeviceModel::STORAGE) 87 | continue; 88 | auto a = i.m_path; 89 | std::cout << a << endl; 90 | d = make_shared(); 91 | d->set_path(a); 92 | d->connect(); 93 | devices.push_back(d); 94 | } 95 | 96 | for (auto d : devices){ 97 | auto res2 = GetDeviceStatus::CommandTransaction::run(d); 98 | std::cout << std::to_string(res2.data().versionInfo.minor) << std::endl; 99 | // nitrokey::proto::stick20::CreateNewKeys::CommandPayload p; 100 | // p.set_defaults(); 101 | // memcpy(p.password, "12345678", 8); 102 | // auto res3 = nitrokey::proto::stick20::CreateNewKeys::CommandTransaction::run(d, p); 103 | } 104 | 105 | for (auto d : devices){ 106 | //TODO watch out for multiple hid_exit calls 107 | d->disconnect(); 108 | } 109 | } 110 | 111 | 112 | TEST_CASE("Use C API", "[BASIC]") { 113 | auto ptr = NK_list_devices(); 114 | auto first_ptr = ptr; 115 | REQUIRE(ptr != nullptr); 116 | 117 | while (ptr) { 118 | std::cout << "Connect with: " << ptr->model << " " << ptr->path << " " 119 | << ptr->serial_number << " | " << NK_connect_with_path(ptr->path) << " | "; 120 | auto status = NK_get_status_as_string(); 121 | std::cout << status << std::endl; 122 | free(status); 123 | ptr = ptr->next; 124 | } 125 | 126 | NK_free_device_info(first_ptr); 127 | } 128 | 129 | 130 | TEST_CASE("Use API", "[BASIC]") { 131 | auto nm = NitrokeyManager::instance(); 132 | nm->set_loglevel(2); 133 | auto v = nm->list_devices(); 134 | REQUIRE(v.size() > 0); 135 | 136 | for (auto i : v) { 137 | std::cout << "Connect with: " << i.m_deviceModel << " " << i.m_path << " " 138 | << i.m_serialNumber << " | " << std::boolalpha << nm->connect_with_path(i.m_path) << " |"; 139 | try { 140 | auto status = nm->get_status(); 141 | std::cout << " " << status.card_serial_u32 << " " 142 | << status.get_card_serial_hex() 143 | << std::endl; 144 | } catch (const LongOperationInProgressException &e) { 145 | std::cout << "long operation in progress on " << i.m_path 146 | << " " << std::to_string(e.progress_bar_value) << std::endl; 147 | } 148 | } 149 | } 150 | 151 | 152 | TEST_CASE("Use Storage API", "[BASIC]") { 153 | auto nm = NitrokeyManager::instance(); 154 | nm->set_loglevel(2); 155 | auto v = nm->list_devices(); 156 | REQUIRE(v.size() > 0); 157 | 158 | for (int i=0; i<10; i++){ 159 | for (auto i : v) { 160 | if (i.m_deviceModel != DeviceModel::STORAGE) 161 | continue; 162 | auto a = i.m_path; 163 | std::cout <<"Connect with: " << a << 164 | " " << std::boolalpha << nm->connect_with_path(a) << " "; 165 | try{ 166 | auto status_storage = nm->get_status_storage(); 167 | std::cout << status_storage.ActiveSmartCardID_u32 168 | << " " << status_storage.ActiveSD_CardID_u32 169 | << std::endl; 170 | 171 | // nm->fill_SD_card_with_random_data("12345678"); 172 | } 173 | catch (const LongOperationInProgressException &e){ 174 | std::cout << "long operation in progress on " << a 175 | << " " << std::to_string(e.progress_bar_value) << std::endl; 176 | // this_thread::sleep_for(1000ms); 177 | } 178 | } 179 | std::cout <<"Iteration: " << i << std::endl; 180 | } 181 | 182 | } 183 | 184 | 185 | TEST_CASE("Use API ID", "[BASIC]") { 186 | auto nm = NitrokeyManager::instance(); 187 | nm->set_loglevel(2); 188 | 189 | auto v = nm->list_devices_by_cpuID(); 190 | REQUIRE(v.size() > 0); 191 | 192 | //no refresh - should not reconnect to new devices 193 | // Scenario: 194 | // 1. Run test 195 | // 2. Remove one of the devices and reinsert it after a while 196 | // 3. Device should not be reconnected and test should not crash 197 | // 4. Remove all devices - test should continue 198 | 199 | for (int j = 0; j < 100; j++) { 200 | for (auto i : v) { 201 | if (!nm->connect_with_ID(i)) continue; 202 | int retry_count = 99; 203 | try { 204 | retry_count = nm->get_admin_retry_count(); 205 | std::cout << j << " " << i << " " << to_string(retry_count) << std::endl; 206 | } 207 | catch (...) { 208 | retry_count = 99; 209 | //pass 210 | } 211 | } 212 | } 213 | std::cout << "finished" << std::endl; 214 | } 215 | 216 | TEST_CASE("Use API ID refresh", "[BASIC]") { 217 | auto nm = NitrokeyManager::instance(); 218 | nm->set_loglevel(2); 219 | 220 | //refresh in each iteration - should reconnect to new devices 221 | // Scenario: 222 | // 1. Run test 223 | // 2. Remove one of the devices and reinsert it after a while 224 | // 3. Device should be reconnected 225 | 226 | for(int j=0; j<100; j++) { 227 | auto v = nm->list_devices_by_cpuID(); 228 | REQUIRE(v.size() > 0); 229 | for (auto i : v) { 230 | nm->connect_with_ID(i); 231 | int retry_count = 99; 232 | try { 233 | retry_count = nm->get_admin_retry_count(); 234 | std::cout << j <<" " << i << " " << to_string(retry_count) << std::endl; 235 | } 236 | catch (...){ 237 | retry_count = 99; 238 | //pass 239 | } 240 | } 241 | } 242 | std::cout << "finished" << std::endl; 243 | } 244 | -------------------------------------------------------------------------------- /unittest/test_offline.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "catch2/catch.hpp" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "../NK_C_API.h" 28 | 29 | using namespace nitrokey::proto; 30 | using namespace nitrokey::device; 31 | 32 | using namespace std; 33 | using namespace nitrokey; 34 | 35 | //This test suite assumes no Pro or Storage devices are connected 36 | 37 | TEST_CASE("Return false on no device connected", "[fast]") { 38 | INFO("This test case assumes no Pro or Storage devices are connected"); 39 | auto stick = make_shared(); 40 | bool connected = true; 41 | REQUIRE_NOTHROW(connected = stick->connect()); 42 | REQUIRE_FALSE(connected); 43 | 44 | auto stick_pro = make_shared(); 45 | REQUIRE_NOTHROW(connected = stick_pro->connect()); 46 | REQUIRE_FALSE(connected); 47 | 48 | 49 | auto i = NitrokeyManager::instance(); 50 | REQUIRE_NOTHROW(connected = i->connect()); 51 | REQUIRE_FALSE(connected); 52 | REQUIRE_FALSE(i->is_connected()); 53 | REQUIRE_FALSE(i->disconnect()); 54 | REQUIRE_FALSE(i->could_current_device_be_enumerated()); 55 | 56 | 57 | int C_connected = 1; 58 | REQUIRE_NOTHROW(C_connected = NK_login_auto()); 59 | REQUIRE(0 == C_connected); 60 | } 61 | 62 | TEST_CASE("Test C++ side behaviour in offline", "[fast]") { 63 | auto i = NitrokeyManager::instance(); 64 | 65 | string serial_number; 66 | REQUIRE_NOTHROW (serial_number = i->get_serial_number()); 67 | REQUIRE(serial_number.empty()); 68 | 69 | REQUIRE_THROWS_AS( 70 | i->get_serial_number_as_u32(), DeviceNotConnected 71 | ); 72 | 73 | REQUIRE_THROWS_AS( 74 | i->get_status(), DeviceNotConnected 75 | ); 76 | 77 | REQUIRE_THROWS_AS( 78 | i->get_HOTP_code(0xFF, ""), InvalidSlotException 79 | ); 80 | 81 | REQUIRE_THROWS_AS( 82 | i->get_TOTP_code(0xFF, ""), InvalidSlotException 83 | ); 84 | 85 | REQUIRE_THROWS_AS( 86 | i->erase_hotp_slot(0xFF, ""), InvalidSlotException 87 | ); 88 | 89 | REQUIRE_THROWS_AS( 90 | i->erase_totp_slot(0xFF, ""), InvalidSlotException 91 | ); 92 | 93 | REQUIRE_THROWS_AS( 94 | i->get_totp_slot_name(0xFF), InvalidSlotException 95 | ); 96 | 97 | REQUIRE_THROWS_AS( 98 | i->get_hotp_slot_name(0xFF), InvalidSlotException 99 | ); 100 | 101 | REQUIRE_THROWS_AS( 102 | i->first_authenticate("123123", "123123"), DeviceNotConnected 103 | ); 104 | 105 | REQUIRE_THROWS_AS( 106 | i->get_connected_device_model(), DeviceNotConnected 107 | ); 108 | 109 | REQUIRE_THROWS_AS( 110 | i->clear_new_sd_card_warning("123123"), DeviceNotConnected 111 | ); 112 | 113 | } 114 | 115 | 116 | TEST_CASE("Test helper function - hex_string_to_byte", "[fast]") { 117 | using namespace nitrokey::misc; 118 | std::vector v; 119 | REQUIRE_NOTHROW(v = hex_string_to_byte("00112233445566")); 120 | const uint8_t test_data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; 121 | REQUIRE(v.size() == sizeof(test_data)); 122 | for (size_t i = 0; i < v.size(); ++i) { 123 | INFO("Position i: " << i); 124 | REQUIRE(v[i] == test_data[i]); 125 | } 126 | } 127 | 128 | #include "test_command_ids_header.h" 129 | TEST_CASE("Test device commands ids", "[fast]") { 130 | // Make sure CommandID values are in sync with firmware's header 131 | 132 | // REQUIRE(STICK20_CMD_START_VALUE == static_cast(CommandID::START_VALUE)); 133 | REQUIRE(STICK20_CMD_ENABLE_CRYPTED_PARI == static_cast(CommandID::ENABLE_CRYPTED_PARI)); 134 | REQUIRE(STICK20_CMD_DISABLE_CRYPTED_PARI == static_cast(CommandID::DISABLE_CRYPTED_PARI)); 135 | REQUIRE(STICK20_CMD_ENABLE_HIDDEN_CRYPTED_PARI == static_cast(CommandID::ENABLE_HIDDEN_CRYPTED_PARI)); 136 | REQUIRE(STICK20_CMD_DISABLE_HIDDEN_CRYPTED_PARI == static_cast(CommandID::DISABLE_HIDDEN_CRYPTED_PARI)); 137 | REQUIRE(STICK20_CMD_ENABLE_FIRMWARE_UPDATE == static_cast(CommandID::ENABLE_FIRMWARE_UPDATE)); 138 | REQUIRE(STICK20_CMD_EXPORT_FIRMWARE_TO_FILE == static_cast(CommandID::EXPORT_FIRMWARE_TO_FILE)); 139 | REQUIRE(STICK20_CMD_GENERATE_NEW_KEYS == static_cast(CommandID::GENERATE_NEW_KEYS)); 140 | REQUIRE(STICK20_CMD_FILL_SD_CARD_WITH_RANDOM_CHARS == static_cast(CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS)); 141 | 142 | REQUIRE(STICK20_CMD_WRITE_STATUS_DATA == static_cast(CommandID::WRITE_STATUS_DATA)); 143 | REQUIRE(STICK20_CMD_ENABLE_READONLY_UNCRYPTED_LUN == static_cast(CommandID::ENABLE_READONLY_UNCRYPTED_LUN)); 144 | REQUIRE(STICK20_CMD_ENABLE_READWRITE_UNCRYPTED_LUN == static_cast(CommandID::ENABLE_READWRITE_UNCRYPTED_LUN)); 145 | 146 | REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX == static_cast(CommandID::SEND_PASSWORD_MATRIX)); 147 | REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX_PINDATA == static_cast(CommandID::SEND_PASSWORD_MATRIX_PINDATA)); 148 | REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX_SETUP == static_cast(CommandID::SEND_PASSWORD_MATRIX_SETUP)); 149 | 150 | REQUIRE(STICK20_CMD_GET_DEVICE_STATUS == static_cast(CommandID::GET_DEVICE_STATUS)); 151 | REQUIRE(STICK20_CMD_SEND_DEVICE_STATUS == static_cast(CommandID::SEND_DEVICE_STATUS)); 152 | 153 | REQUIRE(STICK20_CMD_SEND_HIDDEN_VOLUME_PASSWORD == static_cast(CommandID::SEND_HIDDEN_VOLUME_PASSWORD)); 154 | REQUIRE(STICK20_CMD_SEND_HIDDEN_VOLUME_SETUP == static_cast(CommandID::SEND_HIDDEN_VOLUME_SETUP)); 155 | REQUIRE(STICK20_CMD_SEND_PASSWORD == static_cast(CommandID::SEND_PASSWORD)); 156 | REQUIRE(STICK20_CMD_SEND_NEW_PASSWORD == static_cast(CommandID::SEND_NEW_PASSWORD)); 157 | REQUIRE(STICK20_CMD_CLEAR_NEW_SD_CARD_FOUND == static_cast(CommandID::CLEAR_NEW_SD_CARD_FOUND)); 158 | 159 | REQUIRE(STICK20_CMD_SEND_STARTUP == static_cast(CommandID::SEND_STARTUP)); 160 | REQUIRE(STICK20_CMD_SEND_CLEAR_STICK_KEYS_NOT_INITIATED == static_cast(CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED)); 161 | REQUIRE(STICK20_CMD_SEND_LOCK_STICK_HARDWARE == static_cast(CommandID::SEND_LOCK_STICK_HARDWARE)); 162 | 163 | REQUIRE(STICK20_CMD_PRODUCTION_TEST == static_cast(CommandID::PRODUCTION_TEST)); 164 | REQUIRE(STICK20_CMD_SEND_DEBUG_DATA == static_cast(CommandID::SEND_DEBUG_DATA)); 165 | 166 | REQUIRE(STICK20_CMD_CHANGE_UPDATE_PIN == static_cast(CommandID::CHANGE_UPDATE_PIN)); 167 | 168 | } 169 | 170 | #include "version.h" 171 | TEST_CASE("Test version getter", "[fast]") { 172 | REQUIRE(nitrokey::get_major_library_version() >= 3u); 173 | REQUIRE(nitrokey::get_minor_library_version() >= 3u); 174 | const char *library_version = nitrokey::get_library_version(); 175 | REQUIRE(library_version != nullptr); 176 | CAPTURE(library_version); 177 | 178 | // The library version has to match the pattern returned by git describe: 179 | // v. or v.--g, where is the number 180 | // of commits since the last tag, and is the hash of the current 181 | // commit. (This assumes that all tags have the name v..). 182 | // Optional field is allowed as well. 183 | INFO("This test will fail, if the full git commit version was not collected during library build."); 184 | std::string s = library_version; 185 | std::string version("(pre-)?v[0-9]+\\.[0-9]+(\\.[0-9]+)?"); 186 | std::string git_suffix("(-[0-9]+)+-g[0-9a-z]+"); 187 | std::regex pattern(version + "(" + git_suffix + ")?"); 188 | REQUIRE(std::regex_match(s, pattern)); 189 | } 190 | 191 | TEST_CASE("Connect should not return true after the second attempt", "[fast]") { 192 | int result = 0; 193 | 194 | result = NK_login("S"); 195 | REQUIRE(result == 0); 196 | 197 | result = NK_login_auto(); 198 | REQUIRE(result == 0); 199 | 200 | result = NK_logout(); 201 | REQUIRE(result == 0); 202 | 203 | result = NK_logout(); 204 | REQUIRE(result == 0); 205 | 206 | result = NK_login("P"); 207 | REQUIRE(result == 0); 208 | 209 | result = NK_login_auto(); 210 | REQUIRE(result == 0); 211 | 212 | result = NK_logout(); 213 | REQUIRE(result == 0); 214 | } 215 | -------------------------------------------------------------------------------- /unittest/test_offline.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2019 Nitrokey UG 3 | 4 | This file is part of libnitrokey. 5 | 6 | libnitrokey is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | libnitrokey is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with libnitrokey. If not, see . 18 | 19 | SPDX-License-Identifier: LGPL-3.0 20 | """ 21 | 22 | from misc import gs 23 | import re 24 | 25 | 26 | def test_offline(C_offline): 27 | C_offline.NK_set_debug(False) 28 | C_offline.NK_set_debug_level(4) 29 | assert C_offline.NK_get_major_library_version() == 3 30 | assert C_offline.NK_get_minor_library_version() >= 3 31 | assert C_offline.NK_login_auto() == 0 32 | 33 | libnk_version = gs(C_offline.NK_get_library_version()) 34 | assert libnk_version 35 | print(libnk_version) 36 | 37 | # v3.4.1-29-g1f3d 38 | search = re.search(b'v\d\.\d(\.\d)?', libnk_version) 39 | assert search is not None -------------------------------------------------------------------------------- /unittest/test_pro_bootloader.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from conftest import skip_if_device_version_lower_than, library_device_reconnect 4 | from constants import DefaultPasswords, DeviceErrorCode, LibraryErrors 5 | from helpers import helper_populate_device, helper_check_device_for_data 6 | 7 | 8 | @pytest.mark.firmware 9 | def test_bootloader_password_change_pro_length(C): 10 | skip_if_device_version_lower_than({'P': 11}) 11 | 12 | # Test whether the correct password is set 13 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 14 | # Change to the longest possible password 15 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_LONG) == DeviceErrorCode.STATUS_OK 16 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_LONG, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 17 | # Use longer or shorter passwords than possible 18 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_LONG) == LibraryErrors.TOO_LONG_STRING 19 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TOO_SHORT) == DeviceErrorCode.WRONG_PASSWORD 20 | 21 | 22 | 23 | @pytest.mark.firmware 24 | def test_bootloader_password_change_pro(C): 25 | skip_if_device_version_lower_than({'P': 11}) 26 | assert C.NK_change_firmware_password_pro(b'zxcasd', b'zxcasd') == DeviceErrorCode.WRONG_PASSWORD 27 | 28 | # Revert effects of broken test run, if needed 29 | C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) 30 | 31 | # Change to the same password 32 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 33 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 34 | # Change password 35 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK 36 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 37 | 38 | 39 | @pytest.mark.firmware 40 | def test_bootloader_run_pro_wrong_password(C): 41 | skip_if_device_version_lower_than({'P': 11}) 42 | assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD 43 | 44 | 45 | @pytest.mark.skip_by_default 46 | @pytest.mark.firmware 47 | def test_bootloader_run_pro_real(C): 48 | skip_if_device_version_lower_than({'P': 11}) 49 | # Not enabled due to lack of side-effect removal at this point 50 | assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED 51 | 52 | 53 | @pytest.mark.firmware 54 | def test_bootloader_password_change_pro_too_long(C): 55 | skip_if_device_version_lower_than({'P': 11}) 56 | long_string = b'a' * 100 57 | assert C.NK_change_firmware_password_pro(long_string, long_string) == LibraryErrors.TOO_LONG_STRING 58 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, long_string) == LibraryErrors.TOO_LONG_STRING 59 | 60 | 61 | @pytest.mark.skip_by_default 62 | @pytest.mark.firmware 63 | def test_bootloader_data_retention(C): 64 | skip_if_device_version_lower_than({'P': 11}) 65 | # Not enabled due to lack of side-effect removal at this point 66 | 67 | assert helper_populate_device(C) 68 | assert C.NK_enable_firmware_update_pro(DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_DISCONNECTED 69 | input('Please press ENTER after uploading new firmware to the device') 70 | C = library_device_reconnect(C) 71 | assert helper_check_device_for_data(C) 72 | 73 | 74 | @pytest.mark.firmware 75 | def test_factory_reset_does_not_change_update_password(C): 76 | """ 77 | Check if factory reset changes the update password, which should not happen 78 | """ 79 | skip_if_device_version_lower_than({'P': 13}) 80 | from test_pro import test_factory_reset 81 | # Revert effects of a broken test run, if needed 82 | C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) 83 | 84 | # actual test 85 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK 86 | test_factory_reset(C) 87 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD 88 | assert C.NK_change_firmware_password_pro(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK 89 | 90 | -------------------------------------------------------------------------------- /unittest/test_safe.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "catch2/catch.hpp" 23 | 24 | #include 25 | #include 26 | #include "log.h" 27 | #include "../NK_C_API.h" 28 | 29 | int login; 30 | 31 | TEST_CASE("C API connect", "[BASIC]") { 32 | INFO("This test set assumes either Pro or Storage device is connected."); 33 | INFO("Here should be implemented only tests not changing the device's state, " 34 | "and safe to user data."); 35 | 36 | login = NK_login_auto(); 37 | REQUIRE(login != 0); 38 | NK_set_debug_level(3); 39 | } 40 | 41 | TEST_CASE("Check retry count", "[BASIC]") { 42 | INFO("This test assumes your PINs' attempt counters are set to 3"); 43 | REQUIRE(login != 0); 44 | REQUIRE(NK_get_admin_retry_count() == 3); 45 | REQUIRE(NK_get_user_retry_count() == 3); 46 | } 47 | 48 | void validate_cstring(char *s){ 49 | constexpr uint16_t max_length = 8*1024; 50 | REQUIRE(s != nullptr); 51 | REQUIRE(strnlen(s, max_length) > 0); 52 | REQUIRE(strnlen(s, max_length) < max_length); 53 | std::cout << s << std::endl; 54 | } 55 | 56 | TEST_CASE("Status command for Pro or Storage", "[BASIC]") { 57 | REQUIRE(login != 0); 58 | char* s = nullptr; 59 | 60 | auto const m = NK_get_device_model(); 61 | REQUIRE(m != NK_DISCONNECTED); 62 | if (m == NK_PRO) 63 | s = NK_get_status_as_string(); 64 | else if (m == NK_STORAGE){ 65 | s = NK_get_status_storage_as_string(); 66 | } 67 | 68 | validate_cstring(s); 69 | free(s); 70 | s = nullptr; 71 | } 72 | 73 | TEST_CASE("Device serial", "[BASIC]") { 74 | REQUIRE(login != 0); 75 | char* s = nullptr; 76 | s = NK_device_serial_number(); 77 | validate_cstring(s); 78 | free(s); 79 | s = nullptr; 80 | } 81 | 82 | TEST_CASE("Firmware version", "[BASIC]") { 83 | REQUIRE(login != 0); 84 | // Currently all devices has major version '0' 85 | // No firmware ever had a minor equal to '0' 86 | REQUIRE(NK_get_major_firmware_version() == 0); 87 | REQUIRE(NK_get_minor_firmware_version() != 0); 88 | } 89 | 90 | TEST_CASE("Library version", "[BASIC]") { 91 | REQUIRE(NK_get_major_library_version() == 3); 92 | REQUIRE(NK_get_minor_library_version() >= 4); 93 | } 94 | -------------------------------------------------------------------------------- /unittest/test_strdup.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | // issue: https://github.com/Nitrokey/libnitrokey/issues/110 23 | // tests according to the issue's author, Robin Krahl (robinkrahl) 24 | // suggested run command: valgrind --tool=memcheck --leak-check=full ./test_strdup 25 | 26 | #include 27 | #include 28 | #include "../NK_C_API.h" 29 | #include "catch2/catch.hpp" 30 | 31 | 32 | static const int SHORT_STRING_LENGTH = 10; 33 | 34 | TEST_CASE("Test strdup memory free error", "[BASIC]") 35 | { 36 | NK_set_debug(false); 37 | char *c = NK_get_status_as_string(); /* error --> string literal */ 38 | REQUIRE(c != nullptr); 39 | REQUIRE(strnlen(c, SHORT_STRING_LENGTH) == 0); 40 | puts(c); 41 | free(c); 42 | } 43 | 44 | TEST_CASE("Test strdup memory leak", "[BASIC]") 45 | { 46 | NK_set_debug(false); 47 | bool connected = NK_login_auto() == 1; 48 | if (!connected) return; 49 | 50 | REQUIRE(connected); 51 | char *c = NK_get_status_as_string(); /* no error --> dynamically allocated */ 52 | REQUIRE(c != nullptr); 53 | REQUIRE(strnlen(c, SHORT_STRING_LENGTH) > 0); 54 | puts(c); 55 | free(c); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /version.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "version.h" 23 | 24 | namespace nitrokey { 25 | unsigned int get_major_library_version() { 26 | #ifdef LIBNK_VERSION_MAJOR 27 | return LIBNK_VERSION_MAJOR; 28 | #endif 29 | return 3; 30 | } 31 | 32 | unsigned int get_minor_library_version() { 33 | #ifdef LIBNK_VERSION_MINOR 34 | return LIBNK_VERSION_MINOR; 35 | #endif 36 | return 0; 37 | } 38 | 39 | const char* get_library_version() { 40 | return "unknown"; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /version.cc.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Nitrokey UG 3 | * 4 | * This file is part of libnitrokey. 5 | * 6 | * libnitrokey is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * any later version. 10 | * 11 | * libnitrokey is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with libnitrokey. If not, see . 18 | * 19 | * SPDX-License-Identifier: LGPL-3.0 20 | */ 21 | 22 | #include "version.h" 23 | 24 | namespace nitrokey { 25 | unsigned int get_major_library_version() { 26 | return @PROJECT_VERSION_MAJOR@; 27 | } 28 | 29 | unsigned int get_minor_library_version() { 30 | return @PROJECT_VERSION_MINOR@; 31 | } 32 | 33 | const char* get_library_version() { 34 | return "@PROJECT_VERSION_GIT@"; 35 | } 36 | } 37 | 38 | --------------------------------------------------------------------------------