Codebase list libnitrokey / 2b0e47a
import libnitrokey_3.2.orig.tar.gz Scott Kitterman 6 years ago
54 changed file(s) with 10112 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 *.sw*
1 *.log
2 *.o
3 unittest/build/
4 *.pyc
5 core
6 .cache/
7 .idea/
8 CMakeFiles/
9 /.vs
0 [submodule "unittest/Catch"]
1 path = unittest/Catch
2 url = https://github.com/philsquared/Catch.git
3 [submodule "hidapi"]
4 path = hidapi
5 url = https://github.com/Nitrokey/hidapi.git
0 language: generic
1 sudo: false
2 os: osx
3 env:
4 global:
5 - CF="-DCOMPILE_OFFLINE_TESTS=1 -DERROR_ON_WARNING=ON"
6
7 matrix:
8 include:
9 # - osx_image: xcode7.3 #default
10 # before_install: &brew
11 # - brew update
12 # - brew install hidapi
13 - osx_image: xcode9.1
14 - osx_image: xcode8.2
15 - os: linux
16 dist: trusty
17 env: COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
18 addons:
19 apt:
20 packages:
21 - cmake
22 - libhidapi-dev
23 - g++-5
24 sources: &sources
25 - ubuntu-toolchain-r-test
26 - os: linux
27 dist: trusty
28 env: COMPILER_NAME=gcc CXX=g++-6 CC=gcc-6
29 addons:
30 apt:
31 packages:
32 - cmake
33 - libhidapi-dev
34 - g++-6
35 sources: *sources
36 - os: linux
37 dist: trusty
38 env: COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7
39 addons:
40 apt:
41 packages:
42 - cmake
43 - libhidapi-dev
44 - g++-7
45 sources: *sources
46 - os: linux
47 dist: trusty
48 env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8
49 addons:
50 apt:
51 packages:
52 - cmake
53 - libhidapi-dev
54 - g++-5
55 - clang-3.8
56 sources: *sources
57
58
59 install:
60 - mkdir -p build
61 - cd build
62 # - export CXXFLAGS="${CXX_FLAGS} -Wall -Wextra -Werror" # TODO enable when fixed
63 - ${CXX} --version || true
64 - cmake --version
65 - cmake .. ${CF}
66
67 script:
68 - make -j2
69 - ctest -VV
70 - mkdir install && make install DESTDIR=install
0 # Change Log
1
2 ## [v3.2](https://github.com/Nitrokey/libnitrokey/tree/v3.2) (2018-01-16)
3 [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v3.1...v3.2)
4
5 **Closed issues:**
6
7 - Communication exceptions are not catched in C API [\#89](https://github.com/Nitrokey/libnitrokey/issues/89)
8 - Devices are not detected under Windows [\#88](https://github.com/Nitrokey/libnitrokey/issues/88)
9 - Python bindings example is not working [\#82](https://github.com/Nitrokey/libnitrokey/issues/82)
10 - Missing NK\_C\_API.h in install includes [\#77](https://github.com/Nitrokey/libnitrokey/issues/77)
11 - Handle time-taking commands [\#67](https://github.com/Nitrokey/libnitrokey/issues/67)
12
13 ## [v3.1](https://github.com/Nitrokey/libnitrokey/tree/v3.1) (2017-10-11)
14 [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v3.0...v3.1)
15
16 ## [v3.0](https://github.com/Nitrokey/libnitrokey/tree/v3.0) (2017-10-07)
17 [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v2.0...v3.0)
18
19 **Implemented enhancements:**
20
21 - Support for NK Pro 0.8 [\#51](https://github.com/Nitrokey/libnitrokey/issues/51)
22
23 **Closed issues:**
24
25 - tests are failing [\#71](https://github.com/Nitrokey/libnitrokey/issues/71)
26 - cmake doesn't install any headers [\#70](https://github.com/Nitrokey/libnitrokey/issues/70)
27 - SONAME versioning [\#69](https://github.com/Nitrokey/libnitrokey/issues/69)
28 - Read slot returns invalid counter value for Storage [\#59](https://github.com/Nitrokey/libnitrokey/issues/59)
29 - Return OTP codes as strings [\#57](https://github.com/Nitrokey/libnitrokey/issues/57)
30 - Fix compilation warnings [\#56](https://github.com/Nitrokey/libnitrokey/issues/56)
31 - Add Travis support [\#48](https://github.com/Nitrokey/libnitrokey/issues/48)
32 - Correct get\_time [\#27](https://github.com/Nitrokey/libnitrokey/issues/27)
33 - Move from Makefile to CMake [\#18](https://github.com/Nitrokey/libnitrokey/issues/18)
34
35 ## [v2.0](https://github.com/Nitrokey/libnitrokey/tree/v2.0) (2016-12-12)
36 [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v1.0...v2.0)
37
38 **Implemented enhancements:**
39
40 - Support for Nitrokey Storage - Nitrokey Storage commands [\#14](https://github.com/Nitrokey/libnitrokey/issues/14)
41 - Support for Nitrokey Storage - Nitrokey Pro commands [\#13](https://github.com/Nitrokey/libnitrokey/issues/13)
42
43 **Fixed bugs:**
44
45 - Fails to compile on ubuntu 16.04 [\#46](https://github.com/Nitrokey/libnitrokey/issues/46)
46 - C++ tests do not compile [\#45](https://github.com/Nitrokey/libnitrokey/issues/45)
47 - HOTP counter values limited to 8bit [\#44](https://github.com/Nitrokey/libnitrokey/issues/44)
48 - Device is not released after library function disconnect call [\#43](https://github.com/Nitrokey/libnitrokey/issues/43)
49
50 **Closed issues:**
51
52 - Compilation error on G++6 \(flexible array member\) [\#49](https://github.com/Nitrokey/libnitrokey/issues/49)
53 - No library function for getting device's serial number [\#33](https://github.com/Nitrokey/libnitrokey/issues/33)
54 - Pass binary data \(OTP secrets\) coded as hex values [\#31](https://github.com/Nitrokey/libnitrokey/issues/31)
55 - set\_time not always work [\#29](https://github.com/Nitrokey/libnitrokey/issues/29)
56
57 **Merged pull requests:**
58
59 - Support for Nitrokey Pro 0.8 [\#53](https://github.com/Nitrokey/libnitrokey/pull/53) ([szszszsz](https://github.com/szszszsz))
60 - Support Nitrokey Storage [\#52](https://github.com/Nitrokey/libnitrokey/pull/52) ([szszszsz](https://github.com/szszszsz))
61 - Fix compilation G++6 error [\#50](https://github.com/Nitrokey/libnitrokey/pull/50) ([szszszsz](https://github.com/szszszsz))
62 - Fix compilation warning and error under G++ [\#47](https://github.com/Nitrokey/libnitrokey/pull/47) ([szszszsz](https://github.com/szszszsz))
63 - Support Pro stick commands on Storage device [\#42](https://github.com/Nitrokey/libnitrokey/pull/42) ([szszszsz](https://github.com/szszszsz))
64 - Readme update - dependencies, compilers, format [\#40](https://github.com/Nitrokey/libnitrokey/pull/40) ([szszszsz](https://github.com/szszszsz))
65 - Issue 31 secret as hex [\#36](https://github.com/Nitrokey/libnitrokey/pull/36) ([szszszsz](https://github.com/szszszsz))
66 - Function for getting device's serial number in hex. Fixes \#33 [\#34](https://github.com/Nitrokey/libnitrokey/pull/34) ([szszszsz](https://github.com/szszszsz))
67
68 ## [v1.0](https://github.com/Nitrokey/libnitrokey/tree/v1.0) (2016-08-09)
69 [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v0.9...v1.0)
70
71 **Closed issues:**
72
73 - Automatically detect any connected stick [\#22](https://github.com/Nitrokey/libnitrokey/issues/22)
74 - Security check [\#20](https://github.com/Nitrokey/libnitrokey/issues/20)
75 - Show friendly message when no device is connected and do not abort [\#17](https://github.com/Nitrokey/libnitrokey/issues/17)
76 - Fix PIN protected OTP for Pro [\#15](https://github.com/Nitrokey/libnitrokey/issues/15)
77
78 ## [v0.9](https://github.com/Nitrokey/libnitrokey/tree/v0.9) (2016-08-05)
79 [Full Changelog](https://github.com/Nitrokey/libnitrokey/compare/v0.8...v0.9)
80
81 **Closed issues:**
82
83 - Add README [\#6](https://github.com/Nitrokey/libnitrokey/issues/6)
84 - Support configs for OTP slots [\#19](https://github.com/Nitrokey/libnitrokey/issues/19)
85 - Cover remaining Nitrokey Pro commands in lib and tests [\#16](https://github.com/Nitrokey/libnitrokey/issues/16)
86
87 **Merged pull requests:**
88
89 - waffle.io Badge [\#21](https://github.com/Nitrokey/libnitrokey/pull/21) ([waffle-iron](https://github.com/waffle-iron))
90
91 ## [v0.8](https://github.com/Nitrokey/libnitrokey/tree/v0.8) (2016-08-02)
92 **Implemented enhancements:**
93
94 - Change license to LGPLv3 [\#1](https://github.com/Nitrokey/libnitrokey/issues/1)
95
96 **Closed issues:**
97
98 - Python wrapper for OTP and PINs [\#12](https://github.com/Nitrokey/libnitrokey/issues/12)
99 - Fails to built with default setting on ubuntu 14.04 [\#11](https://github.com/Nitrokey/libnitrokey/issues/11)
100 - Check why packet buffers are not zeroed [\#8](https://github.com/Nitrokey/libnitrokey/issues/8)
101
102 **Merged pull requests:**
103
104 - Reset the HOTP counter [\#9](https://github.com/Nitrokey/libnitrokey/pull/9) ([cornelinux](https://github.com/cornelinux))
105
106
107
108 \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
0 # https://cmake.org/pipermail/cmake/2011-May/044166.html
1 IF(NOT DEFINED CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS)
2 SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON)
3 ENDIF()
4
5 cmake_minimum_required(VERSION 3.1)
6 IF (UNIX)
7 OPTION(ADD_ASAN "Use ASAN to show memory issues" FALSE)
8 OPTION(ADD_TSAN "Use TSAN to show thread issues" FALSE)
9 IF(ADD_ASAN)
10 SET(EXTRA_LIBS ${EXTRA_LIBS} asan )
11 ADD_COMPILE_OPTIONS(-fsanitize=address -fno-omit-frame-pointer)
12 ENDIF()
13 IF(ADD_TSAN)
14 SET(EXTRA_LIBS ${EXTRA_LIBS} tsan )
15 SET(USE_CLANG TRUE)
16 ADD_COMPILE_OPTIONS(-fsanitize=thread -fno-omit-frame-pointer -fPIC -g) #use with clang
17 ENDIF()
18 IF(ADD_TSAN AND ADD_ASAN)
19 message(FATAL_ERROR "TSAN and ASAN cannot be used at the same time")
20 ENDIF()
21 ENDIF()
22
23 project(libnitrokey LANGUAGES C CXX VERSION 3.2.0)
24 set(CMAKE_CXX_STANDARD 14)
25 set(CMAKE_CXX_STANDARD_REQUIRED ON)
26 set(CMAKE_CXX_EXTENSIONS OFF)
27
28
29 include(GNUInstallDirs)
30
31
32
33 IF (NOT CMAKE_BUILD_TYPE)
34 IF(APPLE)
35 # Issues occur when build with enabled optimizations
36 set(CMAKE_BUILD_TYPE Debug)
37 ELSE()
38 set(CMAKE_BUILD_TYPE RelWithDebInfo)
39 ENDIF()
40 ENDIF()
41 MESSAGE("${PROJECT_NAME}: Build type: ${CMAKE_BUILD_TYPE}")
42
43 include_directories(hidapi)
44 include_directories(include)
45 set(SOURCE_FILES
46 include/command.h
47 include/command_id.h
48 include/cxx_semantics.h
49 include/device.h
50 include/device_proto.h
51 include/dissect.h
52 include/log.h
53 include/misc.h
54 include/NitrokeyManager.h
55 include/stick10_commands.h
56 include/stick20_commands.h
57 include/CommandFailedException.h
58 include/LibraryException.h
59 include/LongOperationInProgressException.h
60 include/stick10_commands_0.8.h
61 command_id.cc
62 device.cc
63 log.cc
64 misc.cc
65 NitrokeyManager.cc
66 NK_C_API.h
67 NK_C_API.cc
68 DeviceCommunicationExceptions.cpp)
69
70 set(BUILD_SHARED_LIBS ON CACHE BOOL "Build all libraries as shared")
71 add_library(nitrokey ${SOURCE_FILES})
72
73 IF(APPLE)
74 include_directories(hidapi/hidapi)
75 add_library(hidapi-libusb STATIC hidapi/mac/hid.c )
76 target_link_libraries(hidapi-libusb "-framework CoreFoundation" "-framework IOKit")
77 target_link_libraries(nitrokey hidapi-libusb)
78 ELSEIF(UNIX)
79 # add_library(hidapi-libusb STATIC hidapi/libusb/hid.c )
80 find_package(PkgConfig)
81 pkg_search_module(HIDAPI_LIBUSB REQUIRED hidapi-libusb)
82 target_compile_options(nitrokey PRIVATE ${HIDAPI_LIBUSB_CFLAGS})
83 target_link_libraries(nitrokey ${HIDAPI_LIBUSB_LDFLAGS})
84 ELSEIF(WIN32)
85 include_directories(hidapi/hidapi)
86 add_library(hidapi-libusb STATIC hidapi/windows/hid.c )
87 target_link_libraries(hidapi-libusb setupapi)
88 target_link_libraries(nitrokey hidapi-libusb)
89 ENDIF()
90
91 set_target_properties(nitrokey PROPERTIES
92 VERSION ${libnitrokey_VERSION}
93 SOVERSION ${libnitrokey_VERSION_MAJOR})
94
95 OPTION(ERROR_ON_WARNING "Stop compilation on warning found (not supported for MSVC)" OFF)
96 if (NOT MSVC)
97 set(COMPILE_FLAGS "-Wall -Wno-unused-function -Wcast-qual -Woverloaded-virtual")
98 IF(NOT APPLE)
99 if (ERROR_ON_WARNING)
100 set(COMPILE_FLAGS "${COMPILE_FLAGS} -Werror")
101 endif()
102 ENDIF()
103 SET_TARGET_PROPERTIES(nitrokey PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} )
104 endif()
105
106 OPTION(NO_LOG "Compile without logging functionality and its strings (decreases size)" OFF)
107 IF (NO_LOG)
108 SET_TARGET_PROPERTIES(nitrokey PROPERTIES COMPILE_DEFINITIONS "NO_LOG")
109 ENDIF()
110
111 OPTION(LOG_VOLATILE_DATA "Log volatile data (debug)" OFF)
112 IF (LOG_VOLATILE_DATA)
113 SET_TARGET_PROPERTIES(nitrokey PROPERTIES COMPILE_DEFINITIONS "LOG_VOLATILE_DATA")
114 ENDIF()
115
116
117 file(GLOB LIB_INCLUDES "include/*.h" "NK_C_API.h")
118 install (FILES ${LIB_INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
119 install (TARGETS nitrokey DESTINATION ${CMAKE_INSTALL_LIBDIR})
120
121 # configure and install pkg-config file
122 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libnitrokey.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libnitrokey-1.pc @ONLY)
123 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libnitrokey-1.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
124
125 OPTION(COMPILE_TESTS "Compile tests" FALSE)
126 OPTION(COMPILE_OFFLINE_TESTS "Compile offline tests" FALSE)
127
128 IF(COMPILE_OFFLINE_TESTS OR COMPILE_TESTS)
129 include_directories(unittest/Catch/include)
130 add_library(catch STATIC unittest/catch_main.cpp )
131 ENDIF()
132
133 IF(COMPILE_OFFLINE_TESTS)
134 add_executable (test_offline unittest/test_offline.cc)
135 target_link_libraries (test_offline ${EXTRA_LIBS} nitrokey catch)
136 #run with 'make test' or 'ctest'
137 include (CTest)
138 add_test (runs test_offline)
139 ENDIF()
140
141 IF (COMPILE_TESTS)
142 #needs connected PRO device for success
143 #warning: it may delete data on the device
144 add_executable (test_C_API unittest/test_C_API.cpp)
145 target_link_libraries (test_C_API ${EXTRA_LIBS} nitrokey catch)
146
147 add_executable (test2 unittest/test2.cc)
148 target_link_libraries (test2 ${EXTRA_LIBS} nitrokey catch)
149
150 add_executable (test3 unittest/test3.cc)
151 target_link_libraries (test3 ${EXTRA_LIBS} nitrokey catch)
152
153 add_executable (test_HOTP unittest/test_HOTP.cc)
154 target_link_libraries (test_HOTP ${EXTRA_LIBS} nitrokey catch)
155
156 add_executable (test1 unittest/test.cc)
157 target_link_libraries (test1 ${EXTRA_LIBS} nitrokey catch)
158
159 add_executable (test_issues unittest/test_issues.cc)
160 target_link_libraries (test_issues ${EXTRA_LIBS} nitrokey catch)
161
162 ENDIF()
163
164
165
166 #SET(CPACK_GENERATOR
167 # "DEB;RPM")
168 # build a CPack driven installer package
169 include (InstallRequiredSystemLibraries)
170 set (CPACK_RESOURCE_FILE_LICENSE
171 "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
172 set (CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
173 include (CPack)
0 {
1 // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
2 "configurations": [
3 {
4 "name": "x86-Debug",
5 "generator": "Visual Studio 15 2017",
6 "configurationType" : "Debug",
7 "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
8 "cmakeCommandArgs": "",
9 "buildCommandArgs": "-m -v:minimal"
10 },
11 {
12 "name": "x86-Release",
13 "generator": "Visual Studio 15 2017",
14 "configurationType" : "Release",
15 "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
16 "cmakeCommandArgs": "",
17 "buildCommandArgs": "-m -v:minimal"
18 },
19 {
20 "name": "x64-Debug",
21 "generator": "Visual Studio 15 2017 Win64",
22 "configurationType" : "Debug",
23 "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
24 "cmakeCommandArgs": "",
25 "buildCommandArgs": "-m -v:minimal"
26 },
27 {
28 "name": "x64-Release",
29 "generator": "Visual Studio 15 2017 Win64",
30 "configurationType" : "Release",
31 "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
32 "cmakeCommandArgs": "",
33 "buildCommandArgs": "-m -v:minimal"
34 }
35 ]
36 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include "DeviceCommunicationExceptions.h"
22
23 std::atomic_int DeviceCommunicationException::occurred {0};
0 GNU LESSER GENERAL PUBLIC LICENSE
1 Version 3, 29 June 2007
2
3 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
4 Everyone is permitted to copy and distribute verbatim copies
5 of this license document, but changing it is not allowed.
6
7
8 This version of the GNU Lesser General Public License incorporates
9 the terms and conditions of version 3 of the GNU General Public
10 License, supplemented by the additional permissions listed below.
11
12 0. Additional Definitions.
13
14 As used herein, "this License" refers to version 3 of the GNU Lesser
15 General Public License, and the "GNU GPL" refers to version 3 of the GNU
16 General Public License.
17
18 "The Library" refers to a covered work governed by this License,
19 other than an Application or a Combined Work as defined below.
20
21 An "Application" is any work that makes use of an interface provided
22 by the Library, but which is not otherwise based on the Library.
23 Defining a subclass of a class defined by the Library is deemed a mode
24 of using an interface provided by the Library.
25
26 A "Combined Work" is a work produced by combining or linking an
27 Application with the Library. The particular version of the Library
28 with which the Combined Work was made is also called the "Linked
29 Version".
30
31 The "Minimal Corresponding Source" for a Combined Work means the
32 Corresponding Source for the Combined Work, excluding any source code
33 for portions of the Combined Work that, considered in isolation, are
34 based on the Application, and not on the Linked Version.
35
36 The "Corresponding Application Code" for a Combined Work means the
37 object code and/or source code for the Application, including any data
38 and utility programs needed for reproducing the Combined Work from the
39 Application, but excluding the System Libraries of the Combined Work.
40
41 1. Exception to Section 3 of the GNU GPL.
42
43 You may convey a covered work under sections 3 and 4 of this License
44 without being bound by section 3 of the GNU GPL.
45
46 2. Conveying Modified Versions.
47
48 If you modify a copy of the Library, and, in your modifications, a
49 facility refers to a function or data to be supplied by an Application
50 that uses the facility (other than as an argument passed when the
51 facility is invoked), then you may convey a copy of the modified
52 version:
53
54 a) under this License, provided that you make a good faith effort to
55 ensure that, in the event an Application does not supply the
56 function or data, the facility still operates, and performs
57 whatever part of its purpose remains meaningful, or
58
59 b) under the GNU GPL, with none of the additional permissions of
60 this License applicable to that copy.
61
62 3. Object Code Incorporating Material from Library Header Files.
63
64 The object code form of an Application may incorporate material from
65 a header file that is part of the Library. You may convey such object
66 code under terms of your choice, provided that, if the incorporated
67 material is not limited to numerical parameters, data structure
68 layouts and accessors, or small macros, inline functions and templates
69 (ten or fewer lines in length), you do both of the following:
70
71 a) Give prominent notice with each copy of the object code that the
72 Library is used in it and that the Library and its use are
73 covered by this License.
74
75 b) Accompany the object code with a copy of the GNU GPL and this license
76 document.
77
78 4. Combined Works.
79
80 You may convey a Combined Work under terms of your choice that,
81 taken together, effectively do not restrict modification of the
82 portions of the Library contained in the Combined Work and reverse
83 engineering for debugging such modifications, if you also do each of
84 the following:
85
86 a) Give prominent notice with each copy of the Combined Work that
87 the Library is used in it and that the Library and its use are
88 covered by this License.
89
90 b) Accompany the Combined Work with a copy of the GNU GPL and this license
91 document.
92
93 c) For a Combined Work that displays copyright notices during
94 execution, include the copyright notice for the Library among
95 these notices, as well as a reference directing the user to the
96 copies of the GNU GPL and this license document.
97
98 d) Do one of the following:
99
100 0) Convey the Minimal Corresponding Source under the terms of this
101 License, and the Corresponding Application Code in a form
102 suitable for, and under terms that permit, the user to
103 recombine or relink the Application with a modified version of
104 the Linked Version to produce a modified Combined Work, in the
105 manner specified by section 6 of the GNU GPL for conveying
106 Corresponding Source.
107
108 1) Use a suitable shared library mechanism for linking with the
109 Library. A suitable mechanism is one that (a) uses at run time
110 a copy of the Library already present on the user's computer
111 system, and (b) will operate properly with a modified version
112 of the Library that is interface-compatible with the Linked
113 Version.
114
115 e) Provide Installation Information, but only if you would otherwise
116 be required to provide such information under section 6 of the
117 GNU GPL, and only to the extent that such information is
118 necessary to install and execute a modified version of the
119 Combined Work produced by recombining or relinking the
120 Application with a modified version of the Linked Version. (If
121 you use option 4d0, the Installation Information must accompany
122 the Minimal Corresponding Source and Corresponding Application
123 Code. If you use option 4d1, you must provide the Installation
124 Information in the manner specified by section 6 of the GNU GPL
125 for conveying Corresponding Source.)
126
127 5. Combined Libraries.
128
129 You may place library facilities that are a work based on the
130 Library side by side in a single library together with other library
131 facilities that are not Applications and are not covered by this
132 License, and convey such a combined library under terms of your
133 choice, if you do both of the following:
134
135 a) Accompany the combined library with a copy of the same work based
136 on the Library, uncombined with any other library facilities,
137 conveyed under the terms of this License.
138
139 b) Give prominent notice with the combined library that part of it
140 is a work based on the Library, and explaining where to find the
141 accompanying uncombined form of the same work.
142
143 6. Revised Versions of the GNU Lesser General Public License.
144
145 The Free Software Foundation may publish revised and/or new versions
146 of the GNU Lesser General Public License from time to time. Such new
147 versions will be similar in spirit to the present version, but may
148 differ in detail to address new problems or concerns.
149
150 Each version is given a distinguishing version number. If the
151 Library as you received it specifies that a certain numbered version
152 of the GNU Lesser General Public License "or any later version"
153 applies to it, you have the option of following the terms and
154 conditions either of that published version or of any later version
155 published by the Free Software Foundation. If the Library as you
156 received it does not specify a version number of the GNU Lesser
157 General Public License, you may choose any version of the GNU Lesser
158 General Public License ever published by the Free Software Foundation.
159
160 If the Library as you received it specifies that a proxy can decide
161 whether future versions of the GNU Lesser General Public License shall
162 apply, that proxy's public statement of acceptance of any version is
163 permanent authorization for you to choose that version for the
164 Library.
165
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include "NK_C_API.h"
22 #include <iostream>
23 #include "include/NitrokeyManager.h"
24 #include <cstring>
25 #include "include/LibraryException.h"
26 #include "include/cxx_semantics.h"
27
28 #ifdef _MSC_VER
29 #ifdef _WIN32
30 #pragma message "Using own strndup"
31 char * strndup(const char* str, size_t maxlen) {
32 size_t len = strnlen(str, maxlen);
33 char* dup = (char *)malloc(len + 1);
34 memcpy(dup, str, len);
35 dup[len] = 0;
36 return dup;
37 }
38 #endif
39 #endif
40
41 using namespace nitrokey;
42
43 static uint8_t NK_last_command_status = 0;
44 static const int max_string_field_length = 100;
45
46 template <typename T>
47 T* duplicate_vector_and_clear(std::vector<T> &v){
48 auto d = new T[v.size()];
49 std::copy(v.begin(), v.end(), d);
50 std::fill(v.begin(), v.end(), 0);
51 return d;
52 }
53
54 template <typename T>
55 uint8_t * get_with_array_result(T func){
56 NK_last_command_status = 0;
57 try {
58 return func();
59 }
60 catch (CommandFailedException & commandFailedException){
61 NK_last_command_status = commandFailedException.last_command_status;
62 }
63 catch (LibraryException & libraryException){
64 NK_last_command_status = libraryException.exception_id();
65 }
66 catch (const DeviceCommunicationException &deviceException){
67 NK_last_command_status = 256-deviceException.getType();
68 }
69 return nullptr;
70 }
71
72 template <typename T>
73 const char* get_with_string_result(T func){
74 NK_last_command_status = 0;
75 try {
76 return func();
77 }
78 catch (CommandFailedException & commandFailedException){
79 NK_last_command_status = commandFailedException.last_command_status;
80 }
81 catch (LibraryException & libraryException){
82 NK_last_command_status = libraryException.exception_id();
83 }
84 catch (const DeviceCommunicationException &deviceException){
85 NK_last_command_status = 256-deviceException.getType();
86 }
87 return "";
88 }
89
90 template <typename T>
91 auto get_with_result(T func){
92 NK_last_command_status = 0;
93 try {
94 return func();
95 }
96 catch (CommandFailedException & commandFailedException){
97 NK_last_command_status = commandFailedException.last_command_status;
98 }
99 catch (LibraryException & libraryException){
100 NK_last_command_status = libraryException.exception_id();
101 }
102 catch (const DeviceCommunicationException &deviceException){
103 NK_last_command_status = 256-deviceException.getType();
104 }
105 return static_cast<decltype(func())>(0);
106 }
107
108 template <typename T>
109 uint8_t get_without_result(T func){
110 NK_last_command_status = 0;
111 try {
112 func();
113 return 0;
114 }
115 catch (CommandFailedException & commandFailedException){
116 NK_last_command_status = commandFailedException.last_command_status;
117 }
118 catch (LibraryException & libraryException){
119 NK_last_command_status = libraryException.exception_id();
120 }
121 catch (const InvalidCRCReceived &invalidCRCException){
122 ;
123 }
124 catch (const DeviceCommunicationException &deviceException){
125 NK_last_command_status = 256-deviceException.getType();
126 }
127 return NK_last_command_status;
128 }
129
130
131 #ifdef __cplusplus
132 extern "C" {
133 #endif
134
135 NK_C_API uint8_t NK_get_last_command_status() {
136 auto _copy = NK_last_command_status;
137 NK_last_command_status = 0;
138 return _copy;
139 }
140
141 NK_C_API int NK_login(const char *device_model) {
142 auto m = NitrokeyManager::instance();
143 try {
144 NK_last_command_status = 0;
145 return m->connect(device_model);
146 }
147 catch (CommandFailedException & commandFailedException) {
148 NK_last_command_status = commandFailedException.last_command_status;
149 return commandFailedException.last_command_status;
150 }
151 catch (const DeviceCommunicationException &deviceException){
152 NK_last_command_status = 256-deviceException.getType();
153 cerr << deviceException.what() << endl;
154 return 0;
155 }
156 catch (std::runtime_error &e) {
157 cerr << e.what() << endl;
158 return 0;
159 }
160 return 0;
161 }
162
163 NK_C_API int NK_logout() {
164 auto m = NitrokeyManager::instance();
165 return get_without_result([&]() {
166 m->disconnect();
167 });
168 }
169
170 NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password) {
171 auto m = NitrokeyManager::instance();
172 return get_without_result([&]() {
173 return m->first_authenticate(admin_password, admin_temporary_password);
174 });
175 }
176
177
178 NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password) {
179 auto m = NitrokeyManager::instance();
180 return get_without_result([&]() {
181 m->user_authenticate(user_password, user_temporary_password);
182 });
183 }
184
185 NK_C_API int NK_factory_reset(const char* admin_password) {
186 auto m = NitrokeyManager::instance();
187 return get_without_result([&]() {
188 m->factory_reset(admin_password);
189 });
190 }
191 NK_C_API int NK_build_aes_key(const char* admin_password) {
192 auto m = NitrokeyManager::instance();
193 return get_without_result([&]() {
194 m->build_aes_key(admin_password);
195 });
196 }
197
198 NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) {
199 auto m = NitrokeyManager::instance();
200 return get_without_result([&]() {
201 m->unlock_user_password(admin_password, new_user_password);
202 });
203 }
204
205 NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
206 bool delete_user_password,
207 const char *admin_temporary_password) {
208 auto m = NitrokeyManager::instance();
209 return get_without_result([&]() {
210 return m->write_config(numlock, capslock, scrolllock, enable_user_password, delete_user_password, admin_temporary_password);
211 });
212 }
213
214
215 NK_C_API uint8_t* NK_read_config() {
216 auto m = NitrokeyManager::instance();
217 return get_with_array_result([&]() {
218 auto v = m->read_config();
219 return duplicate_vector_and_clear(v);
220 });
221 }
222
223
224 void clear_string(std::string &s) {
225 std::fill(s.begin(), s.end(), ' ');
226 }
227
228
229 NK_C_API const char * NK_status() {
230 auto m = NitrokeyManager::instance();
231 return get_with_string_result([&]() {
232 string && s = m->get_status_as_string();
233 char * rs = strndup(s.c_str(), max_string_field_length);
234 clear_string(s);
235 return rs;
236 });
237 }
238
239 NK_C_API const char * NK_device_serial_number() {
240 auto m = NitrokeyManager::instance();
241 return get_with_string_result([&]() {
242 string && s = m->get_serial_number();
243 char * rs = strndup(s.c_str(), max_string_field_length);
244 clear_string(s);
245 return rs;
246 });
247 }
248
249 NK_C_API const char * NK_get_hotp_code(uint8_t slot_number) {
250 return NK_get_hotp_code_PIN(slot_number, "");
251 }
252
253 NK_C_API const char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password) {
254 auto m = NitrokeyManager::instance();
255 return get_with_string_result([&]() {
256 string && s = m->get_HOTP_code(slot_number, user_temporary_password);
257 char * rs = strndup(s.c_str(), max_string_field_length);
258 clear_string(s);
259 return rs;
260 });
261 }
262
263 NK_C_API const char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
264 uint8_t last_interval) {
265 return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, "");
266 }
267
268 NK_C_API const char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
269 uint8_t last_interval, const char *user_temporary_password) {
270 auto m = NitrokeyManager::instance();
271 return get_with_string_result([&]() {
272 string && s = m->get_TOTP_code(slot_number, challenge, last_totp_time, last_interval, user_temporary_password);
273 char * rs = strndup(s.c_str(), max_string_field_length);
274 clear_string(s);
275 return rs;
276 });
277 }
278
279 NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) {
280 auto m = NitrokeyManager::instance();
281 return get_without_result([&] {
282 m->erase_hotp_slot(slot_number, temporary_password);
283 });
284 }
285
286 NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) {
287 auto m = NitrokeyManager::instance();
288 return get_without_result([&] {
289 m->erase_totp_slot(slot_number, temporary_password);
290 });
291 }
292
293 NK_C_API int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
294 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
295 const char *temporary_password) {
296 auto m = NitrokeyManager::instance();
297 return get_without_result([&] {
298 m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID, token_ID,
299 temporary_password);
300 });
301 }
302
303 NK_C_API int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
304 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
305 const char *temporary_password) {
306 auto m = NitrokeyManager::instance();
307 return get_without_result([&] {
308 m->write_TOTP_slot(slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID, token_ID,
309 temporary_password);
310 });
311 }
312
313 NK_C_API const char* NK_get_totp_slot_name(uint8_t slot_number) {
314 auto m = NitrokeyManager::instance();
315 return get_with_string_result([&]() {
316 const auto slot_name = m->get_totp_slot_name(slot_number);
317 return slot_name;
318 });
319 }
320 NK_C_API const char* NK_get_hotp_slot_name(uint8_t slot_number) {
321 auto m = NitrokeyManager::instance();
322 return get_with_string_result([&]() {
323 const auto slot_name = m->get_hotp_slot_name(slot_number);
324 return slot_name;
325 });
326 }
327
328 NK_C_API void NK_set_debug(bool state) {
329 auto m = NitrokeyManager::instance();
330 m->set_debug(state);
331 }
332
333
334 NK_C_API void NK_set_debug_level(const int level) {
335 auto m = NitrokeyManager::instance();
336 m->set_loglevel(level);
337 }
338
339 NK_C_API int NK_totp_set_time(uint64_t time) {
340 auto m = NitrokeyManager::instance();
341 return get_without_result([&]() {
342 m->set_time(time);
343 });
344 }
345
346 NK_C_API int NK_totp_get_time() {
347 auto m = NitrokeyManager::instance();
348 return get_without_result([&]() {
349 m->get_time(0); // FIXME check how that should work
350 });
351 }
352
353 NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN) {
354 auto m = NitrokeyManager::instance();
355 return get_without_result([&]() {
356 m->change_admin_PIN(current_PIN, new_PIN);
357 });
358 }
359
360 NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN) {
361 auto m = NitrokeyManager::instance();
362 return get_without_result([&]() {
363 m->change_user_PIN(current_PIN, new_PIN);
364 });
365 }
366
367 NK_C_API int NK_enable_password_safe(const char *user_pin) {
368 auto m = NitrokeyManager::instance();
369 return get_without_result([&]() {
370 m->enable_password_safe(user_pin);
371 });
372 }
373 NK_C_API uint8_t * NK_get_password_safe_slot_status() {
374 auto m = NitrokeyManager::instance();
375 return get_with_array_result([&]() {
376 auto slot_status = m->get_password_safe_slot_status();
377 return duplicate_vector_and_clear(slot_status);
378 });
379
380 }
381
382 NK_C_API uint8_t NK_get_user_retry_count() {
383 auto m = NitrokeyManager::instance();
384 return get_with_result([&]() {
385 return m->get_user_retry_count();
386 });
387 }
388
389 NK_C_API uint8_t NK_get_admin_retry_count() {
390 auto m = NitrokeyManager::instance();
391 return get_with_result([&]() {
392 return m->get_admin_retry_count();
393 });
394 }
395
396 NK_C_API int NK_lock_device() {
397 auto m = NitrokeyManager::instance();
398 return get_without_result([&]() {
399 m->lock_device();
400 });
401 }
402
403 NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number) {
404 auto m = NitrokeyManager::instance();
405 return get_with_string_result([&]() {
406 return m->get_password_safe_slot_name(slot_number);
407 });
408 }
409
410 NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number) {
411 auto m = NitrokeyManager::instance();
412 return get_with_string_result([&]() {
413 return m->get_password_safe_slot_login(slot_number);
414 });
415 }
416 NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number) {
417 auto m = NitrokeyManager::instance();
418 return get_with_string_result([&]() {
419 return m->get_password_safe_slot_password(slot_number);
420 });
421 }
422 NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
423 const char *slot_password) {
424 auto m = NitrokeyManager::instance();
425 return get_without_result([&]() {
426 m->write_password_safe_slot(slot_number, slot_name, slot_login, slot_password);
427 });
428 }
429
430 NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) {
431 auto m = NitrokeyManager::instance();
432 return get_without_result([&]() {
433 m->erase_password_safe_slot(slot_number);
434 });
435 }
436
437 NK_C_API int NK_is_AES_supported(const char *user_password) {
438 auto m = NitrokeyManager::instance();
439 return get_with_result([&]() {
440 return (uint8_t)m->is_AES_supported(user_password);
441 });
442 }
443
444 NK_C_API int NK_login_auto() {
445 auto m = NitrokeyManager::instance();
446 return get_with_result([&]() {
447 return (uint8_t)m->connect();
448 });
449 }
450
451 // storage commands
452
453 NK_C_API int NK_send_startup(uint64_t seconds_from_epoch) {
454 auto m = NitrokeyManager::instance();
455 return get_without_result([&]() {
456 m->send_startup(seconds_from_epoch);
457 });
458 }
459
460 NK_C_API int NK_unlock_encrypted_volume(const char* user_pin) {
461 auto m = NitrokeyManager::instance();
462 return get_without_result([&]() {
463 m->unlock_encrypted_volume(user_pin);
464 });
465 }
466
467 NK_C_API int NK_lock_encrypted_volume() {
468 auto m = NitrokeyManager::instance();
469 return get_without_result([&]() {
470 m->lock_encrypted_volume();
471 });
472 }
473
474 NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password) {
475 auto m = NitrokeyManager::instance();
476 return get_without_result([&]() {
477 m->unlock_hidden_volume(hidden_volume_password);
478 });
479 }
480
481 NK_C_API int NK_lock_hidden_volume() {
482 auto m = NitrokeyManager::instance();
483 return get_without_result([&]() {
484 m->lock_hidden_volume();
485 });
486 }
487
488 NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
489 const char *hidden_volume_password) {
490 auto m = NitrokeyManager::instance();
491 return get_without_result([&]() {
492 m->create_hidden_volume(slot_nr, start_percent, end_percent,
493 hidden_volume_password);
494 });
495 }
496
497 NK_C_API int NK_set_unencrypted_read_only(const char* user_pin) {
498 auto m = NitrokeyManager::instance();
499 return get_without_result([&]() {
500 m->set_unencrypted_read_only(user_pin);
501 });
502 }
503
504 NK_C_API int NK_set_unencrypted_read_write(const char* user_pin) {
505 auto m = NitrokeyManager::instance();
506 return get_without_result([&]() {
507 m->set_unencrypted_read_write(user_pin);
508 });
509 }
510
511 NK_C_API int NK_export_firmware(const char* admin_pin) {
512 auto m = NitrokeyManager::instance();
513 return get_without_result([&]() {
514 m->export_firmware(admin_pin);
515 });
516 }
517
518 NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) {
519 auto m = NitrokeyManager::instance();
520 return get_without_result([&]() {
521 m->clear_new_sd_card_warning(admin_pin);
522 });
523 }
524
525 NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) {
526 auto m = NitrokeyManager::instance();
527 return get_without_result([&]() {
528 m->fill_SD_card_with_random_data(admin_pin);
529 });
530 }
531
532 NK_C_API int NK_change_update_password(const char* current_update_password,
533 const char* new_update_password) {
534 auto m = NitrokeyManager::instance();
535 return get_without_result([&]() {
536 m->change_update_password(current_update_password, new_update_password);
537 });
538 }
539
540 NK_C_API const char* NK_get_status_storage_as_string() {
541 auto m = NitrokeyManager::instance();
542 return get_with_string_result([&]() {
543 return m->get_status_storage_as_string();
544 });
545 }
546
547 NK_C_API const char* NK_get_SD_usage_data_as_string() {
548 auto m = NitrokeyManager::instance();
549 return get_with_string_result([&]() {
550 return m->get_SD_usage_data_as_string();
551 });
552 }
553
554 NK_C_API int NK_get_progress_bar_value() {
555 auto m = NitrokeyManager::instance();
556 return get_with_result([&]() {
557 return m->get_progress_bar_value();
558 });
559 }
560
561 NK_C_API int NK_get_major_firmware_version() {
562 auto m = NitrokeyManager::instance();
563 return get_with_result([&]() {
564 return m->get_minor_firmware_version();
565 });
566 }
567
568
569 #ifdef __cplusplus
570 }
571 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LIBNITROKEY_NK_C_API_H
22 #define LIBNITROKEY_NK_C_API_H
23
24 #include <stdint.h>
25
26 #ifdef _MSC_VER
27 #define NK_C_API __declspec(dllexport)
28 #else
29 #define NK_C_API
30 #endif
31
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 /**
36 * Set debug level of messages written on stderr
37 * @param state state=True - most messages, state=False - only errors level
38 */
39 NK_C_API void NK_set_debug(bool state);
40
41 /**
42 * Set debug level of messages written on stderr
43 * @param level (int) 0-lowest verbosity, 5-highest verbosity
44 */
45 NK_C_API void NK_set_debug_level(const int level);
46
47 /**
48 * Connect to device of given model. Currently library can be connected only to one device at once.
49 * @param device_model char 'S': Nitrokey Storage, 'P': Nitrokey Pro
50 * @return 1 if connected, 0 if wrong model or cannot connect
51 */
52 NK_C_API int NK_login(const char *device_model);
53
54 /**
55 * Connect to first available device, starting checking from Pro 1st to Storage 2nd.
56 * @return 1 if connected, 0 if wrong model or cannot connect
57 */
58 NK_C_API int NK_login_auto();
59
60 /**
61 * Disconnect from the device.
62 * @return command processing error code
63 */
64 NK_C_API int NK_logout();
65
66 /**
67 * Return the debug status string. Debug purposes.
68 * @return command processing error code
69 */
70 NK_C_API const char * NK_status();
71
72 /**
73 * Return the device's serial number string in hex.
74 * @return string device's serial number in hex
75 */
76 NK_C_API const char * NK_device_serial_number();
77
78 /**
79 * Get last command processing status. Useful for commands which returns the results of their own and could not return
80 * an error code.
81 * @return previous command processing error code
82 */
83 NK_C_API uint8_t NK_get_last_command_status();
84
85 /**
86 * Lock device - cancel any user device unlocking.
87 * @return command processing error code
88 */
89 NK_C_API int NK_lock_device();
90
91 /**
92 * Authenticates the user on USER privilages with user_password and sets user's temporary password on device to user_temporary_password.
93 * @param user_password char[25](Pro) current user password
94 * @param user_temporary_password char[25](Pro) user temporary password to be set on device for further communication (authentication command)
95 * @return command processing error code
96 */
97 NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password);
98
99 /**
100 * Authenticates the user on ADMIN privilages with admin_password and sets user's temporary password on device to admin_temporary_password.
101 * @param admin_password char[25](Pro) current administrator PIN
102 * @param admin_temporary_password char[25](Pro) admin temporary password to be set on device for further communication (authentication command)
103 * @return command processing error code
104 */
105 NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password);
106
107 /**
108 * Execute a factory reset.
109 * @param admin_password char[20](Pro) current administrator PIN
110 * @return command processing error code
111 */
112 NK_C_API int NK_factory_reset(const char* admin_password);
113
114 /**
115 * Generates AES key on the device
116 * @param admin_password char[20](Pro) current administrator PIN
117 * @return command processing error code
118 */
119 NK_C_API int NK_build_aes_key(const char* admin_password);
120
121 /**
122 * Unlock user PIN locked after 3 incorrect codes tries.
123 * @param admin_password char[20](Pro) current administrator PIN
124 * @return command processing error code
125 */
126 NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password);
127
128 /**
129 * Write general config to the device
130 * @param numlock set value in range [0-1] to send HOTP code from slot 'numlock' after double pressing numlock
131 * or outside the range to disable this function
132 * @param capslock similar to numlock but with capslock
133 * @param scrolllock similar to numlock but with scrolllock
134 * @param enable_user_password set True to enable OTP PIN protection (request PIN each OTP code request)
135 * @param delete_user_password set True to disable OTP PIN protection (request PIN each OTP code request)
136 * @param admin_temporary_password current admin temporary password
137 * @return command processing error code
138 */
139 NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock,
140 bool enable_user_password, bool delete_user_password, const char *admin_temporary_password);
141
142 /**
143 * Get currently set config - status of function Numlock/Capslock/Scrollock OTP sending and is enabled PIN protected OTP
144 * @see NK_write_config
145 * @return uint8_t general_config[5]:
146 * uint8_t numlock;
147 uint8_t capslock;
148 uint8_t scrolllock;
149 uint8_t enable_user_password;
150 uint8_t delete_user_password;
151
152 */
153 NK_C_API uint8_t* NK_read_config();
154
155 //OTP
156
157 /**
158 * Get name of given TOTP slot
159 * @param slot_number TOTP slot number, slot_number<15
160 * @return char[20](Pro) the name of the slot
161 */
162 NK_C_API const char * NK_get_totp_slot_name(uint8_t slot_number);
163
164 /**
165 *
166 * @param slot_number HOTP slot number, slot_number<3
167 * @return char[20](Pro) the name of the slot
168 */
169 NK_C_API const char * NK_get_hotp_slot_name(uint8_t slot_number);
170
171 /**
172 * Erase HOTP slot data from the device
173 * @param slot_number HOTP slot number, slot_number<3
174 * @param temporary_password admin temporary password
175 * @return command processing error code
176 */
177 NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password);
178
179 /**
180 * Erase TOTP slot data from the device
181 * @param slot_number TOTP slot number, slot_number<15
182 * @param temporary_password admin temporary password
183 * @return command processing error code
184 */
185 NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password);
186
187 /**
188 * Write HOTP slot data to the device
189 * @param slot_number HOTP slot number, slot_number<3
190 * @param slot_name char[15](Pro) desired slot name
191 * @param secret char[20](Pro) 160-bit secret
192 * @param hotp_counter uint32_t starting value of HOTP counter
193 * @param use_8_digits should returned codes be 6 (false) or 8 digits (true)
194 * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock
195 * @param use_tokenID @see token_ID
196 * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section
197 * @param temporary_password char[25](Pro) admin temporary password
198 * @return command processing error code
199 */
200 NK_C_API int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
201 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
202 const char *temporary_password);
203
204 /**
205 * Write TOTP slot data to the device
206 * @param slot_number TOTP slot number, slot_number<15
207 * @param slot_name char[15](Pro) desired slot name
208 * @param secret char[20](Pro) 160-bit secret
209 * @param time_window uint16_t time window for this TOTP
210 * @param use_8_digits should returned codes be 6 (false) or 8 digits (true)
211 * @param use_enter press ENTER key after sending OTP code using double-pressed scroll/num/capslock
212 * @param use_tokenID @see token_ID
213 * @param token_ID @see https://openauthentication.org/token-specs/, 'Class A' section
214 * @param temporary_password char[20](Pro) admin temporary password
215 * @return command processing error code
216 */
217 NK_C_API int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
218 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
219 const char *temporary_password);
220
221 /**
222 * Get HOTP code from the device
223 * @param slot_number HOTP slot number, slot_number<3
224 * @return HOTP code
225 */
226 NK_C_API const char * NK_get_hotp_code(uint8_t slot_number);
227
228 /**
229 * Get HOTP code from the device (PIN protected)
230 * @param slot_number HOTP slot number, slot_number<3
231 * @param user_temporary_password char[25](Pro) user temporary password if PIN protected OTP codes are enabled,
232 * otherwise should be set to empty string - ''
233 * @return HOTP code
234 */
235 NK_C_API const char * NK_get_hotp_code_PIN(uint8_t slot_number, const char *user_temporary_password);
236
237 /**
238 * Get TOTP code from the device
239 * @param slot_number TOTP slot number, slot_number<15
240 * @param challenge TOTP challenge
241 * @param last_totp_time last time
242 * @param last_interval last interval
243 * @return TOTP code
244 */
245 NK_C_API const char * NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
246 uint8_t last_interval);
247
248 /**
249 * Get TOTP code from the device (PIN protected)
250 * @param slot_number TOTP slot number, slot_number<15
251 * @param challenge TOTP challenge
252 * @param last_totp_time last time
253 * @param last_interval last interval
254 * @param user_temporary_password char[25](Pro) user temporary password if PIN protected OTP codes are enabled,
255 * otherwise should be set to empty string - ''
256 * @return TOTP code
257 */
258 NK_C_API const char * NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge,
259 uint64_t last_totp_time, uint8_t last_interval,
260 const char *user_temporary_password);
261
262 /**
263 * Set time on the device (for TOTP requests)
264 * @param time seconds in unix epoch (from 01.01.1970)
265 * @return command processing error code
266 */
267 NK_C_API int NK_totp_set_time(uint64_t time);
268
269 NK_C_API int NK_totp_get_time();
270 //passwords
271 /**
272 * Change administrator PIN
273 * @param current_PIN char[25](Pro) current PIN
274 * @param new_PIN char[25](Pro) new PIN
275 * @return command processing error code
276 */
277 NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN);
278
279 /**
280 * Change user PIN
281 * @param current_PIN char[25](Pro) current PIN
282 * @param new_PIN char[25](Pro) new PIN
283 * @return command processing error code
284 */
285 NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN);
286
287
288 /**
289 * Get retry count of user PIN
290 * @return user PIN retry count
291 */
292 NK_C_API uint8_t NK_get_user_retry_count();
293
294 /**
295 * Get retry count of admin PIN
296 * @return admin PIN retry count
297 */
298 NK_C_API uint8_t NK_get_admin_retry_count();
299 //password safe
300
301 /**
302 * Enable password safe access
303 * @param user_pin char[30](Pro) current user PIN
304 * @return command processing error code
305 */
306 NK_C_API int NK_enable_password_safe(const char *user_pin);
307
308 /**
309 * Get password safe slots' status
310 * @return uint8_t[16] slot statuses - each byte represents one slot with 0 (not programmed) and 1 (programmed)
311 */
312 NK_C_API uint8_t * NK_get_password_safe_slot_status();
313
314 /**
315 * Get password safe slot name
316 * @param slot_number password safe slot number, slot_number<16
317 * @return slot name
318 */
319 NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number);
320
321 /**
322 * Get password safe slot login
323 * @param slot_number password safe slot number, slot_number<16
324 * @return login from the PWS slot
325 */
326 NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number);
327
328 /**
329 * Get the password safe slot password
330 * @param slot_number password safe slot number, slot_number<16
331 * @return password from the PWS slot
332 */
333 NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number);
334
335 /**
336 * Write password safe data to the slot
337 * @param slot_number password safe slot number, slot_number<16
338 * @param slot_name char[11](Pro) name of the slot
339 * @param slot_login char[32](Pro) login string
340 * @param slot_password char[20](Pro) password string
341 * @return command processing error code
342 */
343 NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name,
344 const char *slot_login, const char *slot_password);
345
346 /**
347 * Erase the password safe slot from the device
348 * @param slot_number password safe slot number, slot_number<16
349 * @return command processing error code
350 */
351 NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number);
352
353 /**
354 * Check whether AES is supported by the device
355 * @return 0 for no and 1 for yes
356 */
357 NK_C_API int NK_is_AES_supported(const char *user_password);
358
359 /**
360 * Get device's major firmware version
361 * @return 7,8 for Pro and major for Storage
362 */
363 NK_C_API int NK_get_major_firmware_version();
364
365
366
367 /**
368 * This command is typically run to initiate
369 * communication with the device (altough not required).
370 * It sets time on device and returns its current status
371 * - a combination of set_time and get_status_storage commands
372 * Storage only
373 * @param seconds_from_epoch date and time expressed in seconds
374 */
375 NK_C_API int NK_send_startup(uint64_t seconds_from_epoch);
376
377 /**
378 * Unlock encrypted volume.
379 * Storage only
380 * @param user_pin user pin 20 characters
381 * @return command processing error code
382 */
383 NK_C_API int NK_unlock_encrypted_volume(const char* user_pin);
384
385 /**
386 * Locks encrypted volume
387 * @return command processing error code
388 */
389 NK_C_API int NK_lock_encrypted_volume();
390
391 /**
392 * Unlock hidden volume and lock encrypted volume.
393 * Requires encrypted volume to be unlocked.
394 * Storage only
395 * @param hidden_volume_password 20 characters
396 * @return command processing error code
397 */
398 NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password);
399
400 /**
401 * Locks hidden volume
402 * @return command processing error code
403 */
404 NK_C_API int NK_lock_hidden_volume();
405
406 /**
407 * Create hidden volume.
408 * Requires encrypted volume to be unlocked.
409 * Storage only
410 * @param slot_nr slot number in range 0-3
411 * @param start_percent volume begin expressed in percent of total available storage, int in range 0-99
412 * @param end_percent volume end expressed in percent of total available storage, int in range 1-100
413 * @param hidden_volume_password 20 characters
414 * @return command processing error code
415 */
416 NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
417 const char *hidden_volume_password);
418
419 /**
420 * Make unencrypted volume read-only.
421 * Device hides unencrypted volume for a second therefore make sure
422 * buffers are flushed before running.
423 * Storage only
424 * @param user_pin 20 characters
425 * @return command processing error code
426 */
427 NK_C_API int NK_set_unencrypted_read_only(const char* user_pin);
428
429 /**
430 * Make unencrypted volume read-write.
431 * Device hides unencrypted volume for a second therefore make sure
432 * buffers are flushed before running.
433 * Storage only
434 * @param user_pin 20 characters
435 * @return command processing error code
436 */
437 NK_C_API int NK_set_unencrypted_read_write(const char* user_pin);
438
439 /**
440 * Exports device's firmware to unencrypted volume.
441 * Storage only
442 * @param admin_pin 20 characters
443 * @return command processing error code
444 */
445 NK_C_API int NK_export_firmware(const char* admin_pin);
446
447 /**
448 * Clear new SD card notification. It is set after factory reset.
449 * Storage only
450 * @param admin_pin 20 characters
451 * @return command processing error code
452 */
453 NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin);
454
455 /**
456 * Fill SD card with random data.
457 * Should be done on first stick initialization after creating keys.
458 * Storage only
459 * @param admin_pin 20 characters
460 * @return command processing error code
461 */
462 NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin);
463
464 /**
465 * Change update password.
466 * Update password is used for entering update mode, where firmware
467 * could be uploaded using dfu-programmer or other means.
468 * Storage only
469 * @param current_update_password 20 characters
470 * @param new_update_password 20 characters
471 * @return command processing error code
472 */
473 NK_C_API int NK_change_update_password(const char* current_update_password,
474 const char* new_update_password);
475
476 /**
477 * Get Storage stick status as string.
478 * Storage only
479 * @return string with devices attributes
480 */
481 NK_C_API const char* NK_get_status_storage_as_string();
482
483 /**
484 * Get SD card usage attributes as string.
485 * Usable during hidden volumes creation.
486 * Storage only
487 * @return string with SD card usage attributes
488 */
489 NK_C_API const char* NK_get_SD_usage_data_as_string();
490
491 /**
492 * Get progress value of current long operation.
493 * Storage only
494 * @return int in range 0-100 or -1 if device is not busy
495 */
496 NK_C_API int NK_get_progress_bar_value();
497
498 #ifdef __cplusplus
499 }
500 #endif
501
502 #endif //LIBNITROKEY_NK_C_API_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include <cstring>
22 #include <iostream>
23 #include "include/NitrokeyManager.h"
24 #include "include/LibraryException.h"
25 #include <algorithm>
26 #include <unordered_map>
27 #include <stick20_commands.h>
28 #include "include/misc.h"
29 #include <mutex>
30 #include "include/cxx_semantics.h"
31 #include <functional>
32
33 std::mutex nitrokey::proto::send_receive_mtx;
34
35 namespace nitrokey{
36
37 std::mutex mex_dev_com_manager;
38
39 #ifndef strndup
40 #ifdef _WIN32
41 #pragma message "Using own strndup"
42 char * strndup(const char* str, size_t maxlen){
43 size_t len = strnlen(str, maxlen);
44 char* dup = (char *) malloc(len + 1);
45 memcpy(dup, str, len);
46 dup[len] = 0;
47 return dup;
48 }
49 #endif
50 #endif
51
52 using nitrokey::misc::strcpyT;
53
54 template <typename T>
55 typename T::CommandPayload get_payload(){
56 //Create, initialize and return by value command payload
57 typename T::CommandPayload st;
58 bzero(&st, sizeof(st));
59 return st;
60 }
61
62
63 // package type to auth, auth type [Authorize,UserAuthorize]
64 template <typename S, typename A, typename T>
65 void NitrokeyManager::authorize_packet(T &package, const char *admin_temporary_password, shared_ptr<Device> device){
66 if (!is_authorization_command_supported()){
67 LOG("Authorization command not supported, skipping", Loglevel::WARNING);
68 }
69 auto auth = get_payload<A>();
70 strcpyT(auth.temporary_password, admin_temporary_password);
71 auth.crc_to_authorize = S::CommandTransaction::getCRC(package);
72 A::CommandTransaction::run(device, auth);
73 }
74
75 shared_ptr <NitrokeyManager> NitrokeyManager::_instance = nullptr;
76
77 NitrokeyManager::NitrokeyManager() : device(nullptr)
78 {
79 set_debug(true);
80 }
81 NitrokeyManager::~NitrokeyManager() {
82 }
83
84 bool NitrokeyManager::set_current_device_speed(int retry_delay, int send_receive_delay){
85 if (retry_delay < 20 || send_receive_delay < 20){
86 LOG("Delay set too low: " + to_string(retry_delay) +" "+ to_string(send_receive_delay), Loglevel::WARNING);
87 return false;
88 }
89
90 std::lock_guard<std::mutex> lock(mex_dev_com_manager);
91 if(device == nullptr) {
92 return false;
93 }
94 device->set_receiving_delay(std::chrono::duration<int, std::milli>(send_receive_delay));
95 device->set_retry_delay(std::chrono::duration<int, std::milli>(retry_delay));
96 return true;
97 }
98
99 bool NitrokeyManager::connect() {
100 std::lock_guard<std::mutex> lock(mex_dev_com_manager);
101 vector< shared_ptr<Device> > devices = { make_shared<Stick10>(), make_shared<Stick20>() };
102 for( auto & d : devices ){
103 if (d->connect()){
104 device = std::shared_ptr<Device>(d);
105 }
106 }
107 return device != nullptr;
108 }
109
110
111 void NitrokeyManager::set_log_function(std::function<void(std::string)> log_function){
112 static nitrokey::log::FunctionalLogHandler handler(log_function);
113 nitrokey::log::Log::instance().set_handler(&handler);
114 }
115
116 bool NitrokeyManager::set_default_commands_delay(int delay){
117 if (delay < 20){
118 LOG("Delay set too low: " + to_string(delay), Loglevel::WARNING);
119 return false;
120 }
121 Device::set_default_device_speed(delay);
122 return true;
123 }
124
125 bool NitrokeyManager::connect(const char *device_model) {
126 std::lock_guard<std::mutex> lock(mex_dev_com_manager);
127 LOG(__FUNCTION__, nitrokey::log::Loglevel::DEBUG_L2);
128 switch (device_model[0]){
129 case 'P':
130 device = make_shared<Stick10>();
131 break;
132 case 'S':
133 device = make_shared<Stick20>();
134 break;
135 default:
136 throw std::runtime_error("Unknown model");
137 }
138 return device->connect();
139 }
140
141 shared_ptr<NitrokeyManager> NitrokeyManager::instance() {
142 static std::mutex mutex;
143 std::lock_guard<std::mutex> lock(mutex);
144 if (_instance == nullptr){
145 _instance = make_shared<NitrokeyManager>();
146 }
147 return _instance;
148 }
149
150
151
152 bool NitrokeyManager::disconnect() {
153 std::lock_guard<std::mutex> lock(mex_dev_com_manager);
154 return _disconnect_no_lock();
155 }
156
157 bool NitrokeyManager::_disconnect_no_lock() {
158 //do not use directly without locked mutex,
159 //used by could_be_enumerated, disconnect
160 if (device == nullptr){
161 return false;
162 }
163 const auto res = device->disconnect();
164 device = nullptr;
165 return res;
166 }
167
168 bool NitrokeyManager::is_connected() throw(){
169 std::lock_guard<std::mutex> lock(mex_dev_com_manager);
170 if(device != nullptr){
171 auto connected = device->could_be_enumerated();
172 if(connected){
173 return true;
174 } else {
175 _disconnect_no_lock();
176 return false;
177 }
178 }
179 return false;
180 }
181
182 bool NitrokeyManager::could_current_device_be_enumerated() {
183 std::lock_guard<std::mutex> lock(mex_dev_com_manager);
184 if (device != nullptr) {
185 return device->could_be_enumerated();
186 }
187 return false;
188 }
189
190 void NitrokeyManager::set_loglevel(int loglevel) {
191 loglevel = max(loglevel, static_cast<int>(Loglevel::ERROR));
192 loglevel = min(loglevel, static_cast<int>(Loglevel::DEBUG_L2));
193 Log::instance().set_loglevel(static_cast<Loglevel>(loglevel));
194 }
195
196 void NitrokeyManager::set_loglevel(Loglevel loglevel) {
197 Log::instance().set_loglevel(loglevel);
198 }
199
200 void NitrokeyManager::set_debug(bool state) {
201 if (state){
202 Log::instance().set_loglevel(Loglevel::DEBUG);
203 } else {
204 Log::instance().set_loglevel(Loglevel::ERROR);
205 }
206 }
207
208
209 string NitrokeyManager::get_serial_number() {
210 if (device == nullptr) { return ""; };
211 switch (device->get_device_model()) {
212 case DeviceModel::PRO: {
213 auto response = GetStatus::CommandTransaction::run(device);
214 return nitrokey::misc::toHex(response.data().card_serial_u32);
215 }
216 break;
217
218 case DeviceModel::STORAGE:
219 {
220 auto response = stick20::GetDeviceStatus::CommandTransaction::run(device);
221 return nitrokey::misc::toHex(response.data().ActiveSmartCardID_u32);
222 }
223 break;
224 }
225 return "NA";
226 }
227
228 stick10::GetStatus::ResponsePayload NitrokeyManager::get_status(){
229 try{
230 auto response = GetStatus::CommandTransaction::run(device);
231 return response.data();
232 }
233 catch (DeviceSendingFailure &e){
234 disconnect();
235 throw;
236 }
237 }
238
239 string NitrokeyManager::get_status_as_string() {
240 auto response = GetStatus::CommandTransaction::run(device);
241 return response.data().dissect();
242 }
243
244 string getFilledOTPCode(uint32_t code, bool use_8_digits){
245 stringstream s;
246 s << std::right << std::setw(use_8_digits ? 8 : 6) << std::setfill('0') << code;
247 return s.str();
248 }
249
250 string NitrokeyManager::get_HOTP_code(uint8_t slot_number, const char *user_temporary_password) {
251 if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
252
253 if (is_authorization_command_supported()){
254 auto gh = get_payload<GetHOTP>();
255 gh.slot_number = get_internal_slot_number_for_hotp(slot_number);
256 if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen
257 authorize_packet<GetHOTP, UserAuthorize>(gh, user_temporary_password, device);
258 }
259 auto resp = GetHOTP::CommandTransaction::run(device, gh);
260 return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
261 } else {
262 auto gh = get_payload<stick10_08::GetHOTP>();
263 gh.slot_number = get_internal_slot_number_for_hotp(slot_number);
264 if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0) {
265 strcpyT(gh.temporary_user_password, user_temporary_password);
266 }
267 auto resp = stick10_08::GetHOTP::CommandTransaction::run(device, gh);
268 return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
269 }
270 return "";
271 }
272
273 bool NitrokeyManager::is_valid_hotp_slot_number(uint8_t slot_number) const { return slot_number < 3; }
274 bool NitrokeyManager::is_valid_totp_slot_number(uint8_t slot_number) const { return slot_number < 0x10-1; } //15
275 uint8_t NitrokeyManager::get_internal_slot_number_for_totp(uint8_t slot_number) const { return (uint8_t) (0x20 + slot_number); }
276 uint8_t NitrokeyManager::get_internal_slot_number_for_hotp(uint8_t slot_number) const { return (uint8_t) (0x10 + slot_number); }
277
278
279
280 string NitrokeyManager::get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
281 uint8_t last_interval,
282 const char *user_temporary_password) {
283 if(!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
284 slot_number = get_internal_slot_number_for_totp(slot_number);
285
286 if (is_authorization_command_supported()){
287 auto gt = get_payload<GetTOTP>();
288 gt.slot_number = slot_number;
289 gt.challenge = challenge;
290 gt.last_interval = last_interval;
291 gt.last_totp_time = last_totp_time;
292
293 if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen
294 authorize_packet<GetTOTP, UserAuthorize>(gt, user_temporary_password, device);
295 }
296 auto resp = GetTOTP::CommandTransaction::run(device, gt);
297 return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
298 } else {
299 auto gt = get_payload<stick10_08::GetTOTP>();
300 strcpyT(gt.temporary_user_password, user_temporary_password);
301 gt.slot_number = slot_number;
302 auto resp = stick10_08::GetTOTP::CommandTransaction::run(device, gt);
303 return getFilledOTPCode(resp.data().code, resp.data().use_8_digits);
304 }
305 return "";
306 }
307
308 bool NitrokeyManager::erase_slot(uint8_t slot_number, const char *temporary_password) {
309 if (is_authorization_command_supported()){
310 auto p = get_payload<EraseSlot>();
311 p.slot_number = slot_number;
312 authorize_packet<EraseSlot, Authorize>(p, temporary_password, device);
313 auto resp = EraseSlot::CommandTransaction::run(device,p);
314 } else {
315 auto p = get_payload<stick10_08::EraseSlot>();
316 p.slot_number = slot_number;
317 strcpyT(p.temporary_admin_password, temporary_password);
318 auto resp = stick10_08::EraseSlot::CommandTransaction::run(device,p);
319 }
320 return true;
321 }
322
323 bool NitrokeyManager::erase_hotp_slot(uint8_t slot_number, const char *temporary_password) {
324 if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
325 slot_number = get_internal_slot_number_for_hotp(slot_number);
326 return erase_slot(slot_number, temporary_password);
327 }
328
329 bool NitrokeyManager::erase_totp_slot(uint8_t slot_number, const char *temporary_password) {
330 if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
331 slot_number = get_internal_slot_number_for_totp(slot_number);
332 return erase_slot(slot_number, temporary_password);
333 }
334
335 template <typename T, typename U>
336 void vector_copy_ranged(T& dest, std::vector<U> &vec, size_t begin, size_t elements_to_copy){
337 const size_t d_size = sizeof(dest);
338 if(d_size < elements_to_copy){
339 throw TargetBufferSmallerThanSource(elements_to_copy, d_size);
340 }
341 std::fill(dest, dest+d_size, 0);
342 std::copy(vec.begin() + begin, vec.begin() +begin + elements_to_copy, dest);
343 }
344
345 template <typename T, typename U>
346 void vector_copy(T& dest, std::vector<U> &vec){
347 const size_t d_size = sizeof(dest);
348 if(d_size < vec.size()){
349 throw TargetBufferSmallerThanSource(vec.size(), d_size);
350 }
351 std::fill(dest, dest+d_size, 0);
352 std::copy(vec.begin(), vec.end(), dest);
353 }
354
355 bool NitrokeyManager::write_HOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
356 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
357 const char *temporary_password) {
358 if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
359
360 int internal_slot_number = get_internal_slot_number_for_hotp(slot_number);
361 if (is_authorization_command_supported()){
362 write_HOTP_slot_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID,
363 token_ID, temporary_password);
364 } else {
365 write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, hotp_counter, use_8_digits, use_enter, use_tokenID,
366 token_ID, temporary_password);
367 }
368 return true;
369 }
370
371 void NitrokeyManager::write_HOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret,
372 uint64_t hotp_counter, bool use_8_digits, bool use_enter,
373 bool use_tokenID, const char *token_ID, const char *temporary_password) {
374 auto payload = get_payload<WriteToHOTPSlot>();
375 payload.slot_number = slot_number;
376 auto secret_bin = misc::hex_string_to_byte(secret);
377 vector_copy(payload.slot_secret, secret_bin);
378 strcpyT(payload.slot_name, slot_name);
379 strcpyT(payload.slot_token_id, token_ID);
380 switch (device->get_device_model() ){
381 case DeviceModel::PRO: {
382 payload.slot_counter = hotp_counter;
383 break;
384 }
385 case DeviceModel::STORAGE: {
386 string counter = to_string(hotp_counter);
387 strcpyT(payload.slot_counter_s, counter.c_str());
388 break;
389 }
390 default:
391 LOG(string(__FILE__) + to_string(__LINE__) +
392 string(__FUNCTION__) + string(" Unhandled device model for HOTP")
393 , Loglevel::DEBUG);
394 break;
395 }
396 payload.use_8_digits = use_8_digits;
397 payload.use_enter = use_enter;
398 payload.use_tokenID = use_tokenID;
399
400 authorize_packet<WriteToHOTPSlot, Authorize>(payload, temporary_password, device);
401
402 auto resp = WriteToHOTPSlot::CommandTransaction::run(device, payload);
403 }
404
405 bool NitrokeyManager::write_TOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
406 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
407 const char *temporary_password) {
408 if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
409 int internal_slot_number = get_internal_slot_number_for_totp(slot_number);
410
411 if (is_authorization_command_supported()){
412 write_TOTP_slot_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID,
413 token_ID, temporary_password);
414 } else {
415 write_OTP_slot_no_authorize(internal_slot_number, slot_name, secret, time_window, use_8_digits, use_enter, use_tokenID,
416 token_ID, temporary_password);
417 }
418
419 return true;
420 }
421
422 void NitrokeyManager::write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name,
423 const char *secret,
424 uint64_t counter_or_interval, bool use_8_digits, bool use_enter,
425 bool use_tokenID, const char *token_ID,
426 const char *temporary_password) const {
427
428 auto payload2 = get_payload<stick10_08::SendOTPData>();
429 strcpyT(payload2.temporary_admin_password, temporary_password);
430 strcpyT(payload2.data, slot_name);
431 payload2.setTypeName();
432 stick10_08::SendOTPData::CommandTransaction::run(device, payload2);
433
434 payload2.setTypeSecret();
435 payload2.id = 0;
436 auto secret_bin = misc::hex_string_to_byte(secret);
437 auto remaining_secret_length = secret_bin.size();
438 const auto maximum_OTP_secret_size = 40;
439 if(remaining_secret_length > maximum_OTP_secret_size){
440 throw TargetBufferSmallerThanSource(remaining_secret_length, maximum_OTP_secret_size);
441 }
442
443 while (remaining_secret_length>0){
444 const auto bytesToCopy = std::min(sizeof(payload2.data), remaining_secret_length);
445 const auto start = secret_bin.size() - remaining_secret_length;
446 memset(payload2.data, 0, sizeof(payload2.data));
447 vector_copy_ranged(payload2.data, secret_bin, start, bytesToCopy);
448 stick10_08::SendOTPData::CommandTransaction::run(device, payload2);
449 remaining_secret_length -= bytesToCopy;
450 payload2.id++;
451 }
452
453 auto payload = get_payload<stick10_08::WriteToOTPSlot>();
454 strcpyT(payload.temporary_admin_password, temporary_password);
455 strcpyT(payload.slot_token_id, token_ID);
456 payload.use_8_digits = use_8_digits;
457 payload.use_enter = use_enter;
458 payload.use_tokenID = use_tokenID;
459 payload.slot_counter_or_interval = counter_or_interval;
460 payload.slot_number = internal_slot_number;
461 stick10_08::WriteToOTPSlot::CommandTransaction::run(device, payload);
462 }
463
464 void NitrokeyManager::write_TOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret,
465 uint16_t time_window, bool use_8_digits, bool use_enter,
466 bool use_tokenID, const char *token_ID, const char *temporary_password) {
467 auto payload = get_payload<WriteToTOTPSlot>();
468 payload.slot_number = slot_number;
469 auto secret_bin = misc::hex_string_to_byte(secret);
470 vector_copy(payload.slot_secret, secret_bin);
471 strcpyT(payload.slot_name, slot_name);
472 strcpyT(payload.slot_token_id, token_ID);
473 payload.slot_interval = time_window; //FIXME naming
474 payload.use_8_digits = use_8_digits;
475 payload.use_enter = use_enter;
476 payload.use_tokenID = use_tokenID;
477
478 authorize_packet<WriteToTOTPSlot, Authorize>(payload, temporary_password, device);
479
480 auto resp = WriteToTOTPSlot::CommandTransaction::run(device, payload);
481 }
482
483 const char * NitrokeyManager::get_totp_slot_name(uint8_t slot_number) {
484 if (!is_valid_totp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
485 slot_number = get_internal_slot_number_for_totp(slot_number);
486 return get_slot_name(slot_number);
487 }
488 const char * NitrokeyManager::get_hotp_slot_name(uint8_t slot_number) {
489 if (!is_valid_hotp_slot_number(slot_number)) throw InvalidSlotException(slot_number);
490 slot_number = get_internal_slot_number_for_hotp(slot_number);
491 return get_slot_name(slot_number);
492 }
493
494 static const int max_string_field_length = 2*1024; //storage's status string is ~1k
495
496 const char * NitrokeyManager::get_slot_name(uint8_t slot_number) {
497 auto payload = get_payload<GetSlotName>();
498 payload.slot_number = slot_number;
499 auto resp = GetSlotName::CommandTransaction::run(device, payload);
500 return strndup((const char *) resp.data().slot_name, max_string_field_length);
501 }
502
503 bool NitrokeyManager::first_authenticate(const char *pin, const char *temporary_password) {
504 auto authreq = get_payload<FirstAuthenticate>();
505 strcpyT(authreq.card_password, pin);
506 strcpyT(authreq.temporary_password, temporary_password);
507 FirstAuthenticate::CommandTransaction::run(device, authreq);
508 return true;
509 }
510
511 bool NitrokeyManager::set_time(uint64_t time) {
512 auto p = get_payload<SetTime>();
513 p.reset = 1;
514 p.time = time;
515 SetTime::CommandTransaction::run(device, p);
516 return false;
517 }
518
519 bool NitrokeyManager::get_time(uint64_t time) {
520 auto p = get_payload<SetTime>();
521 p.reset = 0;
522 p.time = time;
523 SetTime::CommandTransaction::run(device, p);
524 return true;
525 }
526
527 void NitrokeyManager::change_user_PIN(const char *current_PIN, const char *new_PIN) {
528 change_PIN_general<ChangeUserPin, PasswordKind::User>(current_PIN, new_PIN);
529 }
530
531 void NitrokeyManager::change_admin_PIN(const char *current_PIN, const char *new_PIN) {
532 change_PIN_general<ChangeAdminPin, PasswordKind::Admin>(current_PIN, new_PIN);
533 }
534
535 template <typename ProCommand, PasswordKind StoKind>
536 void NitrokeyManager::change_PIN_general(const char *current_PIN, const char *new_PIN) {
537 switch (device->get_device_model()){
538 case DeviceModel::PRO:
539 {
540 auto p = get_payload<ProCommand>();
541 strcpyT(p.old_pin, current_PIN);
542 strcpyT(p.new_pin, new_PIN);
543 ProCommand::CommandTransaction::run(device, p);
544 }
545 break;
546 //in Storage change admin/user pin is divided to two commands with 20 chars field len
547 case DeviceModel::STORAGE:
548 {
549 auto p = get_payload<ChangeAdminUserPin20Current>();
550 strcpyT(p.password, current_PIN);
551 p.set_kind(StoKind);
552 auto p2 = get_payload<ChangeAdminUserPin20New>();
553 strcpyT(p2.password, new_PIN);
554 p2.set_kind(StoKind);
555 ChangeAdminUserPin20Current::CommandTransaction::run(device, p);
556 ChangeAdminUserPin20New::CommandTransaction::run(device, p2);
557 }
558 break;
559 }
560
561 }
562
563 void NitrokeyManager::enable_password_safe(const char *user_pin) {
564 //The following command will cancel enabling PWS if it is not supported
565 auto a = get_payload<IsAESSupported>();
566 strcpyT(a.user_password, user_pin);
567 IsAESSupported::CommandTransaction::run(device, a);
568
569 auto p = get_payload<EnablePasswordSafe>();
570 strcpyT(p.user_password, user_pin);
571 EnablePasswordSafe::CommandTransaction::run(device, p);
572 }
573
574 vector <uint8_t> NitrokeyManager::get_password_safe_slot_status() {
575 auto responsePayload = GetPasswordSafeSlotStatus::CommandTransaction::run(device);
576 vector<uint8_t> v = vector<uint8_t>(responsePayload.data().password_safe_status,
577 responsePayload.data().password_safe_status
578 + sizeof(responsePayload.data().password_safe_status));
579 return v;
580 }
581
582 uint8_t NitrokeyManager::get_user_retry_count() {
583 if(device->get_device_model() == DeviceModel::STORAGE){
584 stick20::GetDeviceStatus::CommandTransaction::run(device);
585 }
586 auto response = GetUserPasswordRetryCount::CommandTransaction::run(device);
587 return response.data().password_retry_count;
588 }
589
590 uint8_t NitrokeyManager::get_admin_retry_count() {
591 if(device->get_device_model() == DeviceModel::STORAGE){
592 stick20::GetDeviceStatus::CommandTransaction::run(device);
593 }
594 auto response = GetPasswordRetryCount::CommandTransaction::run(device);
595 return response.data().password_retry_count;
596 }
597
598 void NitrokeyManager::lock_device() {
599 LockDevice::CommandTransaction::run(device);
600 }
601
602 const char *NitrokeyManager::get_password_safe_slot_name(uint8_t slot_number) {
603 if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
604 auto p = get_payload<GetPasswordSafeSlotName>();
605 p.slot_number = slot_number;
606 auto response = GetPasswordSafeSlotName::CommandTransaction::run(device, p);
607 return strndup((const char *) response.data().slot_name, max_string_field_length);
608 }
609
610 bool NitrokeyManager::is_valid_password_safe_slot_number(uint8_t slot_number) const { return slot_number < 16; }
611
612 const char *NitrokeyManager::get_password_safe_slot_login(uint8_t slot_number) {
613 if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
614 auto p = get_payload<GetPasswordSafeSlotLogin>();
615 p.slot_number = slot_number;
616 auto response = GetPasswordSafeSlotLogin::CommandTransaction::run(device, p);
617 return strndup((const char *) response.data().slot_login, max_string_field_length);
618 }
619
620 const char *NitrokeyManager::get_password_safe_slot_password(uint8_t slot_number) {
621 if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
622 auto p = get_payload<GetPasswordSafeSlotPassword>();
623 p.slot_number = slot_number;
624 auto response = GetPasswordSafeSlotPassword::CommandTransaction::run(device, p);
625 return strndup((const char *) response.data().slot_password, max_string_field_length); //FIXME use secure way
626 }
627
628 void NitrokeyManager::write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
629 const char *slot_password) {
630 if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
631 auto p = get_payload<SetPasswordSafeSlotData>();
632 p.slot_number = slot_number;
633 strcpyT(p.slot_name, slot_name);
634 strcpyT(p.slot_password, slot_password);
635 SetPasswordSafeSlotData::CommandTransaction::run(device, p);
636
637 auto p2 = get_payload<SetPasswordSafeSlotData2>();
638 p2.slot_number = slot_number;
639 strcpyT(p2.slot_login_name, slot_login);
640 SetPasswordSafeSlotData2::CommandTransaction::run(device, p2);
641 }
642
643 void NitrokeyManager::erase_password_safe_slot(uint8_t slot_number) {
644 if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
645 auto p = get_payload<ErasePasswordSafeSlot>();
646 p.slot_number = slot_number;
647 ErasePasswordSafeSlot::CommandTransaction::run(device, p);
648 }
649
650 void NitrokeyManager::user_authenticate(const char *user_password, const char *temporary_password) {
651 auto p = get_payload<UserAuthenticate>();
652 strcpyT(p.card_password, user_password);
653 strcpyT(p.temporary_password, temporary_password);
654 UserAuthenticate::CommandTransaction::run(device, p);
655 }
656
657 void NitrokeyManager::build_aes_key(const char *admin_password) {
658 switch (device->get_device_model()) {
659 case DeviceModel::PRO: {
660 auto p = get_payload<BuildAESKey>();
661 strcpyT(p.admin_password, admin_password);
662 BuildAESKey::CommandTransaction::run(device, p);
663 break;
664 }
665 case DeviceModel::STORAGE : {
666 auto p = get_payload<stick20::CreateNewKeys>();
667 strcpyT(p.password, admin_password);
668 p.set_defaults();
669 stick20::CreateNewKeys::CommandTransaction::run(device, p);
670 break;
671 }
672 }
673 }
674
675 void NitrokeyManager::factory_reset(const char *admin_password) {
676 auto p = get_payload<FactoryReset>();
677 strcpyT(p.admin_password, admin_password);
678 FactoryReset::CommandTransaction::run(device, p);
679 }
680
681 void NitrokeyManager::unlock_user_password(const char *admin_password, const char *new_user_password) {
682 switch (device->get_device_model()){
683 case DeviceModel::PRO: {
684 auto p = get_payload<stick10::UnlockUserPassword>();
685 strcpyT(p.admin_password, admin_password);
686 strcpyT(p.user_new_password, new_user_password);
687 stick10::UnlockUserPassword::CommandTransaction::run(device, p);
688 break;
689 }
690 case DeviceModel::STORAGE : {
691 auto p2 = get_payload<ChangeAdminUserPin20Current>();
692 p2.set_defaults();
693 strcpyT(p2.password, admin_password);
694 ChangeAdminUserPin20Current::CommandTransaction::run(device, p2);
695 auto p3 = get_payload<stick20::UnlockUserPin>();
696 p3.set_defaults();
697 strcpyT(p3.password, new_user_password);
698 stick20::UnlockUserPin::CommandTransaction::run(device, p3);
699 break;
700 }
701 }
702 }
703
704
705 void NitrokeyManager::write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
706 bool delete_user_password, const char *admin_temporary_password) {
707 auto p = get_payload<stick10_08::WriteGeneralConfig>();
708 p.numlock = (uint8_t) numlock;
709 p.capslock = (uint8_t) capslock;
710 p.scrolllock = (uint8_t) scrolllock;
711 p.enable_user_password = (uint8_t) enable_user_password;
712 p.delete_user_password = (uint8_t) delete_user_password;
713 if (is_authorization_command_supported()){
714 authorize_packet<stick10_08::WriteGeneralConfig, Authorize>(p, admin_temporary_password, device);
715 } else {
716 strcpyT(p.temporary_admin_password, admin_temporary_password);
717 }
718 stick10_08::WriteGeneralConfig::CommandTransaction::run(device, p);
719 }
720
721 vector<uint8_t> NitrokeyManager::read_config() {
722 auto responsePayload = GetStatus::CommandTransaction::run(device);
723 vector<uint8_t> v = vector<uint8_t>(responsePayload.data().general_config,
724 responsePayload.data().general_config+sizeof(responsePayload.data().general_config));
725 return v;
726 }
727
728 bool NitrokeyManager::is_authorization_command_supported(){
729 //authorization command is supported for versions equal or below:
730 auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({
731 {DeviceModel::PRO, 7},
732 {DeviceModel::STORAGE, 999},
733 });
734 return get_minor_firmware_version() <= m[device->get_device_model()];
735 }
736
737 bool NitrokeyManager::is_320_OTP_secret_supported(){
738 //authorization command is supported for versions equal or below:
739 auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({
740 {DeviceModel::PRO, 8},
741 {DeviceModel::STORAGE, 999},
742 });
743 return get_minor_firmware_version() >= m[device->get_device_model()];
744 }
745
746 DeviceModel NitrokeyManager::get_connected_device_model() const{
747 if (device == nullptr){
748 throw DeviceNotConnected("device not connected");
749 }
750 return device->get_device_model();
751 }
752
753 int NitrokeyManager::get_minor_firmware_version(){
754 switch(device->get_device_model()){
755 case DeviceModel::PRO:{
756 auto status_p = GetStatus::CommandTransaction::run(device);
757 return status_p.data().firmware_version; //7 or 8
758 }
759 case DeviceModel::STORAGE:{
760 auto status = stick20::GetDeviceStatus::CommandTransaction::run(device);
761 return status.data().versionInfo.minor;
762 }
763 }
764 return 0;
765 }
766
767 bool NitrokeyManager::is_AES_supported(const char *user_password) {
768 auto a = get_payload<IsAESSupported>();
769 strcpyT(a.user_password, user_password);
770 IsAESSupported::CommandTransaction::run(device, a);
771 return true;
772 }
773
774 //storage commands
775
776 void NitrokeyManager::send_startup(uint64_t seconds_from_epoch){
777 auto p = get_payload<stick20::SendStartup>();
778 // p.set_defaults(); //set current time
779 p.localtime = seconds_from_epoch;
780 stick20::SendStartup::CommandTransaction::run(device, p);
781 }
782
783 void NitrokeyManager::unlock_encrypted_volume(const char* user_pin){
784 misc::execute_password_command<stick20::EnableEncryptedPartition>(device, user_pin);
785 }
786
787 void NitrokeyManager::unlock_hidden_volume(const char* hidden_volume_password) {
788 misc::execute_password_command<stick20::EnableHiddenEncryptedPartition>(device, hidden_volume_password);
789 }
790
791 //TODO check is encrypted volume unlocked before execution
792 //if not return library exception
793 void NitrokeyManager::create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
794 const char *hidden_volume_password) {
795 auto p = get_payload<stick20::SetupHiddenVolume>();
796 p.SlotNr_u8 = slot_nr;
797 p.StartBlockPercent_u8 = start_percent;
798 p.EndBlockPercent_u8 = end_percent;
799 strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password);
800 stick20::SetupHiddenVolume::CommandTransaction::run(device, p);
801 }
802
803 void NitrokeyManager::set_unencrypted_read_only(const char* user_pin) {
804 misc::execute_password_command<stick20::SendSetReadonlyToUncryptedVolume>(device, user_pin);
805 }
806
807 void NitrokeyManager::set_unencrypted_read_write(const char* user_pin) {
808 misc::execute_password_command<stick20::SendSetReadwriteToUncryptedVolume>(device, user_pin);
809 }
810
811 void NitrokeyManager::export_firmware(const char* admin_pin) {
812 misc::execute_password_command<stick20::ExportFirmware>(device, admin_pin);
813 }
814
815 void NitrokeyManager::enable_firmware_update(const char* firmware_pin) {
816 misc::execute_password_command<stick20::EnableFirmwareUpdate>(device, firmware_pin);
817 }
818
819 void NitrokeyManager::clear_new_sd_card_warning(const char* admin_pin) {
820 misc::execute_password_command<stick20::SendClearNewSdCardFound>(device, admin_pin);
821 }
822
823 void NitrokeyManager::fill_SD_card_with_random_data(const char* admin_pin) {
824 auto p = get_payload<stick20::FillSDCardWithRandomChars>();
825 p.set_defaults();
826 strcpyT(p.admin_pin, admin_pin);
827 stick20::FillSDCardWithRandomChars::CommandTransaction::run(device, p);
828 }
829
830 void NitrokeyManager::change_update_password(const char* current_update_password, const char* new_update_password) {
831 auto p = get_payload<stick20::ChangeUpdatePassword>();
832 strcpyT(p.current_update_password, current_update_password);
833 strcpyT(p.new_update_password, new_update_password);
834 stick20::ChangeUpdatePassword::CommandTransaction::run(device, p);
835 }
836
837 const char * NitrokeyManager::get_status_storage_as_string(){
838 auto p = stick20::GetDeviceStatus::CommandTransaction::run(device);
839 return strndup(p.data().dissect().c_str(), max_string_field_length);
840 }
841
842 stick20::DeviceConfigurationResponsePacket::ResponsePayload NitrokeyManager::get_status_storage(){
843 auto p = stick20::GetDeviceStatus::CommandTransaction::run(device);
844 return p.data();
845 }
846
847 const char * NitrokeyManager::get_SD_usage_data_as_string(){
848 auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device);
849 return strndup(p.data().dissect().c_str(), max_string_field_length);
850 }
851
852 std::pair<uint8_t,uint8_t> NitrokeyManager::get_SD_usage_data(){
853 auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device);
854 return std::make_pair(p.data().WriteLevelMin, p.data().WriteLevelMax);
855 }
856
857 int NitrokeyManager::get_progress_bar_value(){
858 try{
859 stick20::GetDeviceStatus::CommandTransaction::run(device);
860 return -1;
861 }
862 catch (LongOperationInProgressException &e){
863 return e.progress_bar_value;
864 }
865 }
866
867 string NitrokeyManager::get_TOTP_code(uint8_t slot_number, const char *user_temporary_password) {
868 return get_TOTP_code(slot_number, 0, 0, 0, user_temporary_password);
869 }
870
871 stick10::ReadSlot::ResponsePayload NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) {
872 auto p = get_payload<stick10::ReadSlot>();
873 p.slot_number = slot_number;
874 auto data = stick10::ReadSlot::CommandTransaction::run(device, p);
875 return data.data();
876 }
877
878 stick10::ReadSlot::ResponsePayload NitrokeyManager::get_TOTP_slot_data(const uint8_t slot_number) {
879 return get_OTP_slot_data(get_internal_slot_number_for_totp(slot_number));
880 }
881
882 stick10::ReadSlot::ResponsePayload NitrokeyManager::get_HOTP_slot_data(const uint8_t slot_number) {
883 auto slot_data = get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number));
884 if (device->get_device_model() == DeviceModel::STORAGE){
885 //convert counter from string to ull
886 auto counter_s = std::string(slot_data.slot_counter_s, slot_data.slot_counter_s+sizeof(slot_data.slot_counter_s));
887 slot_data.slot_counter = std::stoull(counter_s);
888 }
889 return slot_data;
890 }
891
892 void NitrokeyManager::lock_encrypted_volume() {
893 misc::execute_password_command<stick20::DisableEncryptedPartition>(device, "");
894 }
895
896 void NitrokeyManager::lock_hidden_volume() {
897 misc::execute_password_command<stick20::DisableHiddenEncryptedPartition>(device, "");
898 }
899
900 uint8_t NitrokeyManager::get_SD_card_size() {
901 auto data = stick20::ProductionTest::CommandTransaction::run(device);
902 return data.data().SD_Card_Size_u8;
903 }
904
905
906 }
0 [![Build Status](https://travis-ci.org/Nitrokey/libnitrokey.svg?branch=master)](https://travis-ci.org/Nitrokey/libnitrokey)
1 [![Waffle.io - Columns and their card count](https://badge.waffle.io/Nitrokey/libnitrokey.svg?columns=ready,in%20progress,test,waiting%20for%20feedback)](https://waffle.io/Nitrokey/libnitrokey)
2
3 # libnitrokey
4 libnitrokey is a project to communicate with Nitrokey Pro and Storage devices in a clean and easy manner. Written in C++14, testable with `py.test` and `Catch` frameworks, with C API, Python access (through CFFI and C API, in future with Pybind11).
5
6 The development of this project is aimed to make it itself a living documentation of communication protocol between host and the Nitrokey stick devices. The command packets' format is described here: [Pro v0.7](include/stick10_commands.h), [Pro v0.8](include/stick10_commands_0.8.h), [Storage](include/stick20_commands.h). Handling and additional operations are described here: [NitrokeyManager.cc](NitrokeyManager.cc).
7
8 A C++14 complying compiler is required due to heavy use of variable templates. For feature support tables please check [table 1](https://gcc.gnu.org/projects/cxx-status.html#cxx14) or [table 2](http://en.cppreference.com/w/cpp/compiler_support).
9
10 libnitrokey is developed and tested with the latest compilers: g++ 6.2, clang 3.8. We use Travis CI to test builds also on g++ 5.4 and under OSX compilers starting up from xcode 8.2 environment.
11
12 ## Getting sources
13 This repository uses `git submodules`.
14 To clone please use git's `--recursive` option like in:
15 ```bash
16 git clone --recursive https://github.com/Nitrokey/libnitrokey.git
17 ```
18 or for already cloned repository:
19 ```bash
20 git clone https://github.com/Nitrokey/libnitrokey.git
21 cd libnitrokey
22 git submodule update --init --recursive
23 ```
24
25 ## Dependencies
26 Following libraries are needed to use libnitrokey on Linux (names of the packages on Ubuntu):
27 - libhidapi-dev [(HID API)](http://www.signal11.us/oss/hidapi/)
28 - libusb-1.0-0-dev
29
30
31 ## Compilation
32 libnitrokey uses CMake as its main build system. As a secondary option it offers building through Qt's qMake.
33 ### Qt
34 A Qt's .pro project file is provided for direct compilation and for inclusion to other projects.
35 Using it directly is not recommended due to lack of dependencies check and not implemented library versioning.
36 Compilation is tested with Qt 5.6 and greater.
37
38 Quick start example:
39 ```bash
40 mkdir -p build
41 cd build
42 qmake ..
43 make -j2
44 ```
45
46 ### Windows MS Visual Studio 2017
47 Lately Visual Studio has started handling CMake files directly. After opening the project's directory it should recognize it and initialize build system. Afterwards please run:
48 1. `CMake -> Cache -> View Cache CMakeLists.txt -> CMakeLists.txt` to edit settings
49 2. `CMake -> Build All` to build
50
51 It is possible too to use CMake GUI directly with its settings editor.
52
53 ### CMake
54 To compile please run following sequence of commands:
55 ```bash
56 # assuming current dir is ./libnitrokey/
57 mkdir -p build
58 cd build
59 cmake .. <OPTIONS>
60 make -j2
61 ```
62
63 By default (with empty `<OPTIONS>` string) this will create in `build/` directory a shared library (.so, .dll or .dynlib). If you wish to build static version you can use as `<OPTIONS>` string `-DBUILD_SHARED_LIBS=OFF`.
64
65 All options could be listed with `cmake .. -L` or instead `cmake` a `ccmake ..` tool could be used for configuration (where `..` is the path to directory with `CMakeLists.txt` file). `ccmake` shows also description of the build parameters.
66
67 If you have trouble compiling or running the library you can check [.travis.yml](.travis.yml) file for configuration details. This file is used by Travis CI service to make test builds on OSX and Ubuntu 14.04.
68
69 Other build options (all take either `ON` or `OFF`):
70 * ADD_ASAN - add tests for memory leaks and out-of-bounds access
71 * ADD_TSAN - add tests for threads race, needs USE_CLANG
72 * COMPILE_TESTS - compile C++ tests
73 * COMPILE_OFFLINE_TESTS - compile C++ tests, that do not require any device to be connected
74 * LOG_VOLATILE_DATA (default: OFF) - include secrets in log (PWS passwords, PINs etc)
75 * NO_LOG (default: OFF) - do not compile LOG statements - will make library smaller, but without any diagnostic messages
76
77
78
79 # Using libnitrokey with Python
80 To use libnitrokey with Python a [CFFI](http://cffi.readthedocs.io/en/latest/overview.html) library is required (either 2.7+ or 3.0+). It can be installed with:
81 ```bash
82 pip install --user cffi # for python 2.x
83 pip3 install cffi # for python 3.x
84 ```
85 Just import it, read the C API header and it is done! You have access to the library. Here is an example (in Python 2) printing HOTP code for Pro or Storage device, assuming it is run in root directory [(full example)](python_bindings_example.py):
86 ```python
87 #!/usr/bin/env python2
88 import cffi
89
90 ffi = cffi.FFI()
91 get_string = ffi.string
92
93 def get_library():
94 fp = 'NK_C_API.h' # path to C API header
95
96 declarations = []
97 with open(fp, 'r') as f:
98 declarations = f.readlines()
99
100 cnt = 0
101 a = iter(declarations)
102 for declaration in a:
103 if declaration.strip().startswith('NK_C_API'):
104 declaration = declaration.replace('NK_C_API', '').strip()
105 while ';' not in declaration:
106 declaration += (next(a)).strip()
107 # print(declaration)
108 ffi.cdef(declaration, override=True)
109 cnt +=1
110 print('Imported {} declarations'.format(cnt))
111
112
113 C = None
114 import os, sys
115 path_build = os.path.join(".", "build")
116 paths = [
117 os.environ.get('LIBNK_PATH', None),
118 os.path.join(path_build,"libnitrokey.so"),
119 os.path.join(path_build,"libnitrokey.dylib"),
120 os.path.join(path_build,"libnitrokey.dll"),
121 os.path.join(path_build,"nitrokey.dll"),
122 ]
123 for p in paths:
124 if not p: continue
125 print("Trying " +p)
126 p = os.path.abspath(p)
127 if os.path.exists(p):
128 print("Found: "+p)
129 C = ffi.dlopen(p)
130 break
131 else:
132 print("File does not exist: " + p)
133 if not C:
134 print("No library file found")
135 sys.exit(1)
136
137 return C
138
139
140 def get_hotp_code(lib, i):
141 return lib.NK_get_hotp_code(i)
142
143
144 libnitrokey = get_library()
145 libnitrokey.NK_set_debug(False) # do not show debug messages (log library only)
146
147 hotp_slot_code = get_hotp_code(libnitrokey, 1)
148 print('Getting HOTP code from Nitrokey device: ')
149 print(hotp_slot_code)
150 libnitrokey.NK_logout() # disconnect device
151 ```
152
153 In case no devices are connected, a friendly message will be printed.
154 All available functions for C and Python are listed in [NK_C_API.h](NK_C_API.h). Please check `Documentation` section below.
155
156 ## Documentation
157 The documentation of C API is included in the sources (could be generated with doxygen if requested).
158 Please check [NK_C_API.h](NK_C_API.h) (C API) for high level commands and [include/NitrokeyManager.h](include/NitrokeyManager.h) (C++ API). All devices' commands are listed along with packet format in [include/stick10_commands.h](include/stick10_commands.h) and [include/stick20_commands.h](include/stick20_commands.h) respectively for Nitrokey Pro and Nitrokey Storage products.
159
160 # Tests
161 Warning! Before you run unittests please either change both your Admin and User PINs on your Nitrostick to defaults (`12345678` and `123456` respectively) or change the values in tests source code. If you do not change them the tests might lock your device and lose your data. If it's too late, you can reset your Nitrokey using instructions from [homepage](https://www.nitrokey.com/de/documentation/how-reset-nitrokey).
162
163 ## Python tests
164 libnitrokey has a great suite of tests written in Python 3 under the path: `unittest/test_*.py`:
165 * `test_pro.py` - contains tests of OTP, Password Safe and PIN control functionality. Could be run on both Pro and Storage devices.
166 * `test_storage.py` - contains tests of Encrypted Volumes functionality. Could be run only on Storage.
167 The tests themselves show how to handle common requests to device.
168 Before running please install all required libraries with:
169 ```bash
170 cd unittest
171 pip install --user -r requirements.txt
172 ```
173 To run them please execute:
174 ```bash
175 # substitute <dev> with either pro or storage
176 py.test -v test_<dev>.py
177 # more specific use - run tests containing in name <test_name> 5 times:
178 py.test -v test_<dev>.py -k <test_name> --count 5
179
180 ```
181 For additional documentation please check the following for [py.test installation](http://doc.pytest.org/en/latest/getting-started.html). For better coverage [randomly plugin](https://pypi.python.org/pypi/pytest-randomly) is installed - it randomizes the test order allowing to detect unseen dependencies between the tests.
182
183 ## C++ tests
184 There are also some unit tests implemented in C++, placed in unittest directory. They are not written as extensively as Python tests and are rather more a C++ low level interface check, often not using C++ API from `NitrokeyManager.cc`. Some of them are: [test_HOTP.cc](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test_HOTP.cc),
185 [test.cc](https://github.com/Nitrokey/libnitrokey/blob/master/unittest/test.cc).
186 Unit tests were written and tested on Ubuntu 16.04/16.10/17.04. To run them just execute binaries built in ./libnitrokey/build dir after enabling them by passing `-DCOMPILE_TESTS` option like in `cmake .. -DCOMPILE_TESTS && make`.
187
188
189 The documentation of how it works could be found in nitrokey-app project's README on Github:
190 [Nitrokey-app - internals](https://github.com/Nitrokey/nitrokey-app/blob/master/README.md#internals).
191
192 To peek/debug communication with device running nitrokey-app (0.x branch) in debug mode (`-d` switch) and checking the logs
193 (right click on tray icon and then 'Debug') might be helpful. Latest Nitrokey App (1.x branch) uses libnitrokey to communicate with device. Once run with `--dl 3` (3 or higher; range 0-5) it will print all communication to the console. Additionally crosschecking with
194 firmware code should show how things works:
195 [report_protocol.c](https://github.com/Nitrokey/nitrokey-pro-firmware/blob/master/src/keyboard/report_protocol.c)
196 (for Nitrokey Pro, for Storage similarly).
197
198 # Known issues / tasks
199 * Currently only one device can be connected at a time (experimental work could be found in `wip-multiple_devices` branch),
200 * C++ API needs some reorganization to C++ objects (instead of pointers to arrays). This will be also preparing for integration with Pybind11,
201 * Fix compilation warnings.
202
203 Other tasks might be listed either in [TODO](TODO) file or on project's issues page.
204
205 # License
206 This project is licensed under LGPL version 3. License text could be found under [LICENSE](LICENSE) file.
207
208 # Roadmap
209 To check what issues will be fixed and when please check [milestones](https://github.com/Nitrokey/libnitrokey/milestones) page.
0 use strings instead of char* and vectors instead of others
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include <assert.h>
22 #include "command_id.h"
23
24 namespace nitrokey {
25 namespace proto {
26
27 const char *commandid_to_string(CommandID id) {
28 switch (id) {
29 case CommandID::GET_STATUS:
30 return "GET_STATUS";
31 case CommandID::WRITE_TO_SLOT:
32 return "WRITE_TO_SLOT";
33 case CommandID::READ_SLOT_NAME:
34 return "READ_SLOT_NAME";
35 case CommandID::READ_SLOT:
36 return "READ_SLOT";
37 case CommandID::GET_CODE:
38 return "GET_CODE";
39 case CommandID::WRITE_CONFIG:
40 return "WRITE_CONFIG";
41 case CommandID::ERASE_SLOT:
42 return "ERASE_SLOT";
43 case CommandID::FIRST_AUTHENTICATE:
44 return "FIRST_AUTHENTICATE";
45 case CommandID::AUTHORIZE:
46 return "AUTHORIZE";
47 case CommandID::GET_PASSWORD_RETRY_COUNT:
48 return "GET_PASSWORD_RETRY_COUNT";
49 case CommandID::CLEAR_WARNING:
50 return "CLEAR_WARNING";
51 case CommandID::SET_TIME:
52 return "SET_TIME";
53 case CommandID::TEST_COUNTER:
54 return "TEST_COUNTER";
55 case CommandID::TEST_TIME:
56 return "TEST_TIME";
57 case CommandID::USER_AUTHENTICATE:
58 return "USER_AUTHENTICATE";
59 case CommandID::GET_USER_PASSWORD_RETRY_COUNT:
60 return "GET_USER_PASSWORD_RETRY_COUNT";
61 case CommandID::USER_AUTHORIZE:
62 return "USER_AUTHORIZE";
63 case CommandID::UNLOCK_USER_PASSWORD:
64 return "UNLOCK_USER_PASSWORD";
65 case CommandID::LOCK_DEVICE:
66 return "LOCK_DEVICE";
67 case CommandID::FACTORY_RESET:
68 return "FACTORY_RESET";
69 case CommandID::CHANGE_USER_PIN:
70 return "CHANGE_USER_PIN";
71 case CommandID::CHANGE_ADMIN_PIN:
72 return "CHANGE_ADMIN_PIN";
73
74 case CommandID::ENABLE_CRYPTED_PARI:
75 return "ENABLE_CRYPTED_PARI";
76 case CommandID::DISABLE_CRYPTED_PARI:
77 return "DISABLE_CRYPTED_PARI";
78 case CommandID::ENABLE_HIDDEN_CRYPTED_PARI:
79 return "ENABLE_HIDDEN_CRYPTED_PARI";
80 case CommandID::DISABLE_HIDDEN_CRYPTED_PARI:
81 return "DISABLE_HIDDEN_CRYPTED_PARI";
82 case CommandID::ENABLE_FIRMWARE_UPDATE:
83 return "ENABLE_FIRMWARE_UPDATE";
84 case CommandID::EXPORT_FIRMWARE_TO_FILE:
85 return "EXPORT_FIRMWARE_TO_FILE";
86 case CommandID::GENERATE_NEW_KEYS:
87 return "GENERATE_NEW_KEYS";
88 case CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS:
89 return "FILL_SD_CARD_WITH_RANDOM_CHARS";
90
91 case CommandID::WRITE_STATUS_DATA:
92 return "WRITE_STATUS_DATA";
93 case CommandID::ENABLE_READONLY_UNCRYPTED_LUN:
94 return "ENABLE_READONLY_UNCRYPTED_LUN";
95 case CommandID::ENABLE_READWRITE_UNCRYPTED_LUN:
96 return "ENABLE_READWRITE_UNCRYPTED_LUN";
97
98 case CommandID::SEND_PASSWORD_MATRIX:
99 return "SEND_PASSWORD_MATRIX";
100 case CommandID::SEND_PASSWORD_MATRIX_PINDATA:
101 return "SEND_PASSWORD_MATRIX_PINDATA";
102 case CommandID::SEND_PASSWORD_MATRIX_SETUP:
103 return "SEND_PASSWORD_MATRIX_SETUP";
104
105 case CommandID::GET_DEVICE_STATUS:
106 return "GET_DEVICE_STATUS";
107 case CommandID::SEND_DEVICE_STATUS:
108 return "SEND_DEVICE_STATUS";
109
110 case CommandID::SEND_HIDDEN_VOLUME_PASSWORD:
111 return "SEND_HIDDEN_VOLUME_PASSWORD";
112 case CommandID::SEND_HIDDEN_VOLUME_SETUP:
113 return "SEND_HIDDEN_VOLUME_SETUP";
114 case CommandID::SEND_PASSWORD:
115 return "SEND_PASSWORD";
116 case CommandID::SEND_NEW_PASSWORD:
117 return "SEND_NEW_PASSWORD";
118 case CommandID::CLEAR_NEW_SD_CARD_FOUND:
119 return "CLEAR_NEW_SD_CARD_FOUND";
120
121 case CommandID::SEND_STARTUP:
122 return "SEND_STARTUP";
123 case CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED:
124 return "SEND_CLEAR_STICK_KEYS_NOT_INITIATED";
125 case CommandID::SEND_LOCK_STICK_HARDWARE:
126 return "SEND_LOCK_STICK_HARDWARE";
127
128 case CommandID::PRODUCTION_TEST:
129 return "PRODUCTION_TEST";
130 case CommandID::SEND_DEBUG_DATA:
131 return "SEND_DEBUG_DATA";
132
133 case CommandID::CHANGE_UPDATE_PIN:
134 return "CHANGE_UPDATE_PIN";
135
136 case CommandID::GET_PW_SAFE_SLOT_STATUS:
137 return "GET_PW_SAFE_SLOT_STATUS";
138 case CommandID::GET_PW_SAFE_SLOT_NAME:
139 return "GET_PW_SAFE_SLOT_NAME";
140 case CommandID::GET_PW_SAFE_SLOT_PASSWORD:
141 return "GET_PW_SAFE_SLOT_PASSWORD";
142 case CommandID::GET_PW_SAFE_SLOT_LOGINNAME:
143 return "GET_PW_SAFE_SLOT_LOGINNAME";
144 case CommandID::SET_PW_SAFE_SLOT_DATA_1:
145 return "SET_PW_SAFE_SLOT_DATA_1";
146 case CommandID::SET_PW_SAFE_SLOT_DATA_2:
147 return "SET_PW_SAFE_SLOT_DATA_2";
148 case CommandID::PW_SAFE_ERASE_SLOT:
149 return "PW_SAFE_ERASE_SLOT";
150 case CommandID::PW_SAFE_ENABLE:
151 return "PW_SAFE_ENABLE";
152 case CommandID::PW_SAFE_INIT_KEY:
153 return "PW_SAFE_INIT_KEY";
154 case CommandID::PW_SAFE_SEND_DATA:
155 return "PW_SAFE_SEND_DATA";
156 case CommandID::SD_CARD_HIGH_WATERMARK:
157 return "SD_CARD_HIGH_WATERMARK";
158 case CommandID::DETECT_SC_AES:
159 return "DETECT_SC_AES";
160 case CommandID::NEW_AES_KEY:
161 return "NEW_AES_KEY";
162 case CommandID::WRITE_TO_SLOT_2:
163 return "WRITE_TO_SLOT_2";
164 break;
165 case CommandID::SEND_OTP_DATA:
166 return "SEND_OTP_DATA";
167 break;
168 }
169 return "UNKNOWN";
170 }
171 }
172 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include <chrono>
22 #include <thread>
23 #include <cstddef>
24 #include <stdexcept>
25 #include "hidapi/hidapi.h"
26 #include "include/misc.h"
27 #include "include/device.h"
28 #include "include/log.h"
29 #include <mutex>
30 #include "DeviceCommunicationExceptions.h"
31 #include "device.h"
32
33 std::mutex mex_dev_com;
34
35 using namespace nitrokey::device;
36 using namespace nitrokey::log;
37 using namespace std::chrono;
38
39 std::atomic_int Device::instances_count{0};
40 std::chrono::milliseconds Device::default_delay {0} ;
41
42 Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
43 const milliseconds send_receive_delay, const int retry_receiving_count,
44 const milliseconds retry_timeout)
45 :
46 last_command_status(0),
47 m_vid(vid),
48 m_pid(pid),
49 m_model(model),
50 m_retry_sending_count(1),
51 m_retry_receiving_count(retry_receiving_count),
52 m_retry_timeout(retry_timeout),
53 m_send_receive_delay(send_receive_delay),
54 mp_devhandle(nullptr)
55 {
56 instances_count++;
57 }
58
59 bool Device::disconnect() {
60 //called in object's destructor
61 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
62 std::lock_guard<std::mutex> lock(mex_dev_com);
63 return _disconnect();
64 }
65
66 bool Device::_disconnect() {
67 LOG(std::string(__FUNCTION__) + std::string(m_model == DeviceModel::PRO ? "PRO" : "STORAGE"), Loglevel::DEBUG_L2);
68 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
69
70 LOG(std::string("Disconnection success: ") + std::to_string(mp_devhandle == nullptr), Loglevel::DEBUG_L2);
71 if(mp_devhandle == nullptr) return false;
72
73 hid_close(mp_devhandle);
74 mp_devhandle = nullptr;
75 #ifndef __APPLE__
76 if (instances_count == 1){
77 LOG(std::string("Calling hid_exit"), Loglevel::DEBUG_L2);
78 hid_exit();
79 }
80 #endif
81 return true;
82 }
83
84 bool Device::connect() {
85 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
86 std::lock_guard<std::mutex> lock(mex_dev_com);
87 return _connect();
88 }
89
90 bool Device::_connect() {
91 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
92
93 // hid_init(); // done automatically on hid_open
94 mp_devhandle = hid_open(m_vid, m_pid, nullptr);
95 const bool success = mp_devhandle != nullptr;
96 LOG(std::string("Connection success: ") + std::to_string(success), Loglevel::DEBUG_L2);
97 return success;
98 }
99
100 int Device::send(const void *packet) {
101 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
102 std::lock_guard<std::mutex> lock(mex_dev_com);
103 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
104
105 int send_feature_report = -1;
106
107 for (int i = 0; i < 3 && send_feature_report < 0; ++i) {
108 if (mp_devhandle == nullptr) {
109 LOG(std::string("Connection fail") , Loglevel::DEBUG_L2);
110 throw DeviceNotConnected("Attempted HID send on an invalid descriptor.");
111 }
112 send_feature_report = hid_send_feature_report(
113 mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE);
114 if (send_feature_report < 0) _reconnect();
115 //add thread sleep?
116 LOG(std::string("Sending attempt: ")+std::to_string(i+1) + " / 3" , Loglevel::DEBUG_L2);
117 }
118 return send_feature_report;
119 }
120
121 int Device::recv(void *packet) {
122 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
123 std::lock_guard<std::mutex> lock(mex_dev_com);
124 LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
125 int status;
126 int retry_count = 0;
127
128 for (;;) {
129 if (mp_devhandle == nullptr){
130 LOG(std::string("Connection fail") , Loglevel::DEBUG_L2);
131 throw DeviceNotConnected("Attempted HID receive on an invalid descriptor.");
132 }
133
134 status = (hid_get_feature_report(mp_devhandle, (unsigned char *)(packet),
135 HID_REPORT_SIZE));
136
137 auto pwherr = hid_error(mp_devhandle);
138 std::wstring wherr = (pwherr != nullptr) ? pwherr : L"No error message";
139 std::string herr(wherr.begin(), wherr.end());
140 LOG(std::string("libhid error message: ") + herr,
141 Loglevel::DEBUG_L2);
142
143 if (status > 0) break; // success
144 if (retry_count++ >= m_retry_receiving_count) {
145 LOG(
146 "Maximum retry count reached: " + std::to_string(retry_count),
147 Loglevel::WARNING);
148 LOG(
149 std::string("Counter stats: ") + m_counters.get_as_string(),
150 Loglevel::DEBUG);
151 break;
152 }
153 _reconnect();
154 LOG("Retrying... " + std::to_string(retry_count),
155 Loglevel::DEBUG);
156 std::this_thread::sleep_for(m_retry_timeout);
157 }
158
159 return status;
160 }
161
162 bool Device::could_be_enumerated() {
163 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
164 std::lock_guard<std::mutex> lock(mex_dev_com);
165 if (mp_devhandle==nullptr){
166 return false;
167 }
168 #ifndef __APPLE__
169 auto pInfo = hid_enumerate(m_vid, m_pid);
170 if (pInfo != nullptr){
171 hid_free_enumeration(pInfo);
172 return true;
173 }
174 return false;
175 #else
176 // alternative for OSX
177 unsigned char buf[1];
178 return hid_read_timeout(mp_devhandle, buf, sizeof(buf), 20) != -1;
179 #endif
180 }
181
182 void Device::show_stats() {
183 auto s = m_counters.get_as_string();
184 LOG(s, Loglevel::DEBUG_L2);
185 }
186
187 void Device::_reconnect() {
188 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
189 ++m_counters.low_level_reconnect;
190 _disconnect();
191 _connect();
192 }
193
194 Device::~Device() {
195 show_stats();
196 disconnect();
197 instances_count--;
198 }
199
200 void Device::set_default_device_speed(int delay) {
201 default_delay = std::chrono::duration<int, std::milli>(delay);
202 }
203
204
205 void Device::set_receiving_delay(const std::chrono::milliseconds delay){
206 std::lock_guard<std::mutex> lock(mex_dev_com);
207 m_send_receive_delay = delay;
208 }
209
210 void Device::set_retry_delay(const std::chrono::milliseconds delay){
211 std::lock_guard<std::mutex> lock(mex_dev_com);
212 m_retry_timeout = delay;
213 }
214
215 Stick10::Stick10():
216 Device(0x20a0, 0x4108, DeviceModel::PRO, 100ms, 5, 100ms)
217 {
218 setDefaultDelay();
219 }
220
221
222 Stick20::Stick20():
223 Device(0x20a0, 0x4109, DeviceModel::STORAGE, 40ms, 25, 40ms)
224 {
225 setDefaultDelay();
226 }
227
228 #include <sstream>
229 #define p(x) ss << #x << " " << x << ", ";
230 std::string Device::ErrorCounters::get_as_string() {
231 std::stringstream ss;
232 p(total_comm_runs);
233 p(communication_successful);
234 ss << "(";
235 p(command_successful_recv);
236 p(command_result_not_equal_0_recv);
237 ss << "), ";
238 p(sends_executed);
239 p(recv_executed);
240 p(successful_storage_commands);
241 p(total_retries);
242 ss << "(";
243 p(busy);
244 p(busy_progressbar);
245 p(CRC_other_than_awaited);
246 p(wrong_CRC);
247 ss << "), ";
248 p(low_level_reconnect);
249 p(sending_error);
250 p(receiving_error);
251 return ss.str();
252 }
253
254 void Device::setDefaultDelay() {
255 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
256
257 auto count = default_delay.count();
258 if (count != 0){
259 LOG("Setting default delay to " + std::to_string(count), Loglevel::DEBUG_L2);
260 m_retry_timeout = default_delay;
261 m_send_receive_delay = default_delay;
262 }
263 }
264
265 #undef p
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LIBNITROKEY_COMMANDFAILEDEXCEPTION_H
22 #define LIBNITROKEY_COMMANDFAILEDEXCEPTION_H
23
24 #include <exception>
25 #include <cstdint>
26 #include "log.h"
27 #include "command_id.h"
28
29 using cs = nitrokey::proto::stick10::command_status;
30
31 class CommandFailedException : public std::exception {
32 public:
33 const uint8_t last_command_id;
34 const uint8_t last_command_status;
35
36 CommandFailedException(uint8_t last_command_id, uint8_t last_command_status) :
37 last_command_id(last_command_id),
38 last_command_status(last_command_status){
39 LOG(std::string("CommandFailedException, status: ")+ std::to_string(last_command_status), nitrokey::log::Loglevel::DEBUG);
40 }
41
42 virtual const char *what() const throw() {
43 return "Command execution has failed on device";
44 }
45
46
47 bool reason_timestamp_warning() const throw(){
48 return last_command_status == static_cast<uint8_t>(cs::timestamp_warning);
49 }
50
51 bool reason_AES_not_initialized() const throw(){
52 return last_command_status == static_cast<uint8_t>(cs::AES_dec_failed);
53 }
54
55 bool reason_not_authorized() const throw(){
56 return last_command_status == static_cast<uint8_t>(cs::not_authorized);
57 }
58
59 bool reason_slot_not_programmed() const throw(){
60 return last_command_status == static_cast<uint8_t>(cs::slot_not_programmed);
61 }
62
63 bool reason_wrong_password() const throw(){
64 return last_command_status == static_cast<uint8_t>(cs::wrong_password);
65 }
66
67 };
68
69
70 #endif //LIBNITROKEY_COMMANDFAILEDEXCEPTION_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21
22 #ifndef LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H
23 #define LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H
24
25 #include <atomic>
26 #include <exception>
27 #include <stdexcept>
28 #include <string>
29
30
31 class DeviceCommunicationException: public std::runtime_error
32 {
33 std::string message;
34 static std::atomic_int occurred;
35 public:
36 DeviceCommunicationException(std::string _msg): std::runtime_error(_msg), message(_msg){
37 ++occurred;
38 }
39 uint8_t getType() const {return 1;};
40 // virtual const char* what() const throw() override {
41 // return message.c_str();
42 // }
43 static bool has_occurred(){ return occurred > 0; };
44 static void reset_occurred_flag(){ occurred = 0; };
45 };
46
47 class DeviceNotConnected: public DeviceCommunicationException {
48 public:
49 DeviceNotConnected(std::string msg) : DeviceCommunicationException(msg){}
50 uint8_t getType() const {return 2;};
51 };
52
53 class DeviceSendingFailure: public DeviceCommunicationException {
54 public:
55 DeviceSendingFailure(std::string msg) : DeviceCommunicationException(msg){}
56 uint8_t getType() const {return 3;};
57 };
58
59 class DeviceReceivingFailure: public DeviceCommunicationException {
60 public:
61 DeviceReceivingFailure(std::string msg) : DeviceCommunicationException(msg){}
62 uint8_t getType() const {return 4;};
63 };
64
65 class InvalidCRCReceived: public DeviceReceivingFailure {
66 public:
67 InvalidCRCReceived(std::string msg) : DeviceReceivingFailure(msg){}
68 uint8_t getType() const {return 5;};
69 };
70
71
72 #endif //LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LIBNITROKEY_LIBRARYEXCEPTION_H
22 #define LIBNITROKEY_LIBRARYEXCEPTION_H
23
24 #include <exception>
25 #include <cstdint>
26 #include <string>
27 #include "log.h"
28
29 class LibraryException: std::exception {
30 public:
31 virtual uint8_t exception_id()= 0;
32 };
33
34 class TargetBufferSmallerThanSource: public LibraryException {
35 public:
36 virtual uint8_t exception_id() override {
37 return 203;
38 }
39
40 public:
41 size_t source_size;
42 size_t target_size;
43
44 TargetBufferSmallerThanSource(
45 size_t source_size, size_t target_size
46 ) : source_size(source_size), target_size(target_size) {}
47
48 virtual const char *what() const throw() override {
49 std::string s = " ";
50 auto ts = [](size_t x){ return std::to_string(x); };
51 std::string msg = std::string("Target buffer size is smaller than source: [source size, buffer size]")
52 +s+ ts(source_size) +s+ ts(target_size);
53 return msg.c_str();
54 }
55
56 };
57
58 class InvalidHexString : public LibraryException {
59 public:
60 virtual uint8_t exception_id() override {
61 return 202;
62 }
63
64 public:
65 uint8_t invalid_char;
66
67 InvalidHexString (uint8_t invalid_char) : invalid_char( invalid_char) {}
68
69 virtual const char *what() const throw() override {
70 return "Invalid character in hex string";
71 }
72
73 };
74
75 class InvalidSlotException : public LibraryException {
76 public:
77 virtual uint8_t exception_id() override {
78 return 201;
79 }
80
81 public:
82 uint8_t slot_selected;
83
84 InvalidSlotException(uint8_t slot_selected) : slot_selected(slot_selected) {}
85
86 virtual const char *what() const throw() override {
87 return "Wrong slot selected";
88 }
89
90 };
91
92
93
94 class TooLongStringException : public LibraryException {
95 public:
96 virtual uint8_t exception_id() override {
97 return 200;
98 }
99
100 std::size_t size_source;
101 std::size_t size_destination;
102 std::string message;
103
104 TooLongStringException(size_t size_source, size_t size_destination, const std::string &message = "") : size_source(
105 size_source), size_destination(size_destination), message(message) {
106 LOG(std::string("TooLongStringException, size diff: ")+ std::to_string(size_source-size_destination), nitrokey::log::Loglevel::DEBUG);
107
108 }
109
110 virtual const char *what() const throw() override {
111 //TODO add sizes and message data to final message
112 return "Too long string has been supplied as an argument";
113 }
114
115 };
116
117 #endif //LIBNITROKEY_LIBRARYEXCEPTION_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H
22 #define LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H
23
24 #include "CommandFailedException.h"
25
26 class LongOperationInProgressException : public CommandFailedException {
27
28 public:
29 unsigned char progress_bar_value;
30
31 LongOperationInProgressException(
32 unsigned char _command_id, uint8_t last_command_status, unsigned char _progress_bar_value)
33 : CommandFailedException(_command_id, last_command_status), progress_bar_value(_progress_bar_value){
34 LOG(
35 std::string("LongOperationInProgressException, progress bar status: ")+
36 std::to_string(progress_bar_value), nitrokey::log::Loglevel::DEBUG);
37 }
38 virtual const char *what() const throw() {
39 return "Device returned busy status with long operation in progress";
40 }
41 };
42
43
44 #endif //LIBNITROKEY_LONGOPERATIONINPROGRESSEXCEPTION_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LIBNITROKEY_NITROKEYMANAGER_H
22 #define LIBNITROKEY_NITROKEYMANAGER_H
23
24 #include "device.h"
25 #include "log.h"
26 #include "device_proto.h"
27 #include "stick10_commands.h"
28 #include "stick10_commands_0.8.h"
29 #include "stick20_commands.h"
30 #include <vector>
31 #include <memory>
32
33 namespace nitrokey {
34 using namespace nitrokey::device;
35 using namespace std;
36 using namespace nitrokey::proto::stick10;
37 using namespace nitrokey::proto::stick20;
38 using namespace nitrokey::proto;
39 using namespace nitrokey::log;
40
41
42 #ifdef __WIN32
43 char * strndup(const char* str, size_t maxlen);
44 #endif
45
46 class NitrokeyManager {
47 public:
48 static shared_ptr <NitrokeyManager> instance();
49
50 bool first_authenticate(const char *pin, const char *temporary_password);
51 bool write_HOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
52 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
53 const char *temporary_password);
54 bool write_TOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
55 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
56 const char *temporary_password);
57 string get_HOTP_code(uint8_t slot_number, const char *user_temporary_password);
58 string get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
59 uint8_t last_interval,
60 const char *user_temporary_password);
61 string get_TOTP_code(uint8_t slot_number, const char *user_temporary_password);
62 stick10::ReadSlot::ResponsePayload get_TOTP_slot_data(const uint8_t slot_number);
63 stick10::ReadSlot::ResponsePayload get_HOTP_slot_data(const uint8_t slot_number);
64
65 bool set_time(uint64_t time);
66 bool get_time(uint64_t time = 0);
67 bool erase_totp_slot(uint8_t slot_number, const char *temporary_password);
68 bool erase_hotp_slot(uint8_t slot_number, const char *temporary_password);
69 bool connect(const char *device_model);
70 bool connect();
71 bool disconnect();
72 bool is_connected() throw() ;
73 bool could_current_device_be_enumerated();
74 bool set_default_commands_delay(int delay);
75
76 DeviceModel get_connected_device_model() const;
77 void set_debug(bool state);
78 stick10::GetStatus::ResponsePayload get_status();
79 string get_status_as_string();
80 string get_serial_number();
81
82 const char * get_totp_slot_name(uint8_t slot_number);
83 const char * get_hotp_slot_name(uint8_t slot_number);
84
85 void change_user_PIN(const char *current_PIN, const char *new_PIN);
86 void change_admin_PIN(const char *current_PIN, const char *new_PIN);
87
88 void enable_password_safe(const char *user_pin);
89
90 vector <uint8_t> get_password_safe_slot_status();
91
92 uint8_t get_admin_retry_count();
93 uint8_t get_user_retry_count();
94
95 void lock_device();
96
97 const char *get_password_safe_slot_name(uint8_t slot_number);
98 const char *get_password_safe_slot_password(uint8_t slot_number);
99 const char *get_password_safe_slot_login(uint8_t slot_number);
100
101 void
102 write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
103 const char *slot_password);
104
105 void erase_password_safe_slot(uint8_t slot_number);
106
107 void user_authenticate(const char *user_password, const char *temporary_password);
108
109 void factory_reset(const char *admin_password);
110
111 void build_aes_key(const char *admin_password);
112
113 void unlock_user_password(const char *admin_password, const char *new_user_password);
114
115 void write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
116 bool delete_user_password, const char *admin_temporary_password);
117
118 vector<uint8_t> read_config();
119
120 bool is_AES_supported(const char *user_password);
121
122 void unlock_encrypted_volume(const char *user_password);
123 void lock_encrypted_volume();
124
125 void unlock_hidden_volume(const char *hidden_volume_password);
126 void lock_hidden_volume();
127
128 void set_unencrypted_read_only(const char *user_pin);
129
130 void set_unencrypted_read_write(const char *user_pin);
131
132 void export_firmware(const char *admin_pin);
133 void enable_firmware_update(const char *firmware_pin);
134
135 void clear_new_sd_card_warning(const char *admin_pin);
136
137 void fill_SD_card_with_random_data(const char *admin_pin);
138
139 uint8_t get_SD_card_size();
140
141 void change_update_password(const char *current_update_password, const char *new_update_password);
142
143 void create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
144 const char *hidden_volume_password);
145
146 void send_startup(uint64_t seconds_from_epoch);
147
148 const char * get_status_storage_as_string();
149 stick20::DeviceConfigurationResponsePacket::ResponsePayload get_status_storage();
150
151 const char *get_SD_usage_data_as_string();
152 std::pair<uint8_t,uint8_t> get_SD_usage_data();
153
154
155 int get_progress_bar_value();
156
157 ~NitrokeyManager();
158 bool is_authorization_command_supported();
159 bool is_320_OTP_secret_supported();
160
161
162 template <typename S, typename A, typename T>
163 void authorize_packet(T &package, const char *admin_temporary_password, shared_ptr<Device> device);
164 int get_minor_firmware_version();
165
166 explicit NitrokeyManager();
167 void set_log_function(std::function<void(std::string)> log_function);
168 private:
169
170 static shared_ptr <NitrokeyManager> _instance;
171 std::shared_ptr<Device> device;
172
173 stick10::ReadSlot::ResponsePayload get_OTP_slot_data(const uint8_t slot_number);
174 bool is_valid_hotp_slot_number(uint8_t slot_number) const;
175 bool is_valid_totp_slot_number(uint8_t slot_number) const;
176 bool is_valid_password_safe_slot_number(uint8_t slot_number) const;
177 uint8_t get_internal_slot_number_for_hotp(uint8_t slot_number) const;
178 uint8_t get_internal_slot_number_for_totp(uint8_t slot_number) const;
179 bool erase_slot(uint8_t slot_number, const char *temporary_password);
180 const char * get_slot_name(uint8_t slot_number);
181
182 template <typename ProCommand, PasswordKind StoKind>
183 void change_PIN_general(const char *current_PIN, const char *new_PIN);
184
185 void write_HOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
186 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
187 const char *temporary_password);
188
189 void write_TOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
190 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
191 const char *temporary_password);
192
193 void write_OTP_slot_no_authorize(uint8_t internal_slot_number, const char *slot_name, const char *secret,
194 uint64_t counter_or_interval,
195 bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
196 const char *temporary_password) const;
197 bool _disconnect_no_lock();
198
199 public:
200 bool set_current_device_speed(int retry_delay, int send_receive_delay);
201 void set_loglevel(Loglevel loglevel);
202
203 void set_loglevel(int loglevel);
204 };
205 }
206
207
208
209 #endif //LIBNITROKEY_NITROKEYMANAGER_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef COMMAND_H
22 #define COMMAND_H
23 #include <string>
24 #include "command_id.h"
25 #include "cxx_semantics.h"
26
27 #define print_to_ss(x) ( ss << " " << (#x) <<":\t" << (x) << std::endl );
28 #ifdef LOG_VOLATILE_DATA
29 #define print_to_ss_volatile(x) print_to_ss(x);
30 #else
31 #define print_to_ss_volatile(x) ( ss << " " << (#x) <<":\t" << "***********" << std::endl );
32 #endif
33 #define hexdump_to_ss(x) (ss << #x":\n"\
34 << ::nitrokey::misc::hexdump((const uint8_t *) (&x), sizeof x, false));
35
36 namespace nitrokey {
37 namespace proto {
38
39 template<CommandID cmd_id>
40 class Command : semantics::non_constructible {
41 public:
42 constexpr static CommandID command_id() { return cmd_id; }
43
44 template<typename T>
45 std::string dissect(const T &) {
46 return std::string("Payload dissection is unavailable");
47 }
48 };
49
50 namespace stick20{
51 enum class PasswordKind : uint8_t {
52 User = 'P',
53 Admin = 'A',
54 AdminPrefixed
55 };
56
57 template<CommandID cmd_id, PasswordKind Tpassword_kind = PasswordKind::User, int password_length = 20>
58 class PasswordCommand : public Command<cmd_id> {
59 constexpr static CommandID _command_id() { return cmd_id; }
60 public:
61 struct CommandPayload {
62 uint8_t kind;
63 uint8_t password[password_length];
64
65 std::string dissect() const {
66 std::stringstream ss;
67 print_to_ss( kind );
68 print_to_ss_volatile(password);
69 return ss.str();
70 }
71 void set_kind_admin() {
72 kind = (uint8_t) 'A';
73 }
74 void set_kind_admin_prefixed() {
75 kind = (uint8_t) 'P';
76 }
77 void set_kind_user() {
78 kind = (uint8_t) 'P';
79 }
80
81 void set_defaults(){
82 set_kind(Tpassword_kind);
83 }
84
85 void set_kind(PasswordKind password_kind){
86 switch (password_kind){
87 case PasswordKind::Admin:
88 set_kind_admin();
89 break;
90 case PasswordKind::User:
91 set_kind_user();
92 break;
93 case PasswordKind::AdminPrefixed:
94 set_kind_admin_prefixed();
95 break;
96 }
97 };
98
99 } __packed;
100
101 //typedef Transaction<Command<cmd_id>::command_id(), struct CommandPayload, struct EmptyPayload>
102 // CommandTransaction;
103 using CommandTransaction = Transaction<cmd_id, CommandPayload, EmptyPayload>;
104 //using CommandTransaction = Transaction<_command_id(), CommandPayload, EmptyPayload>;
105
106 };
107 }
108 }
109 }
110
111 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef COMMAND_ID_H
22 #define COMMAND_ID_H
23 #include <stdint.h>
24
25 namespace nitrokey {
26 namespace proto {
27 namespace stick20 {
28 enum class device_status : uint8_t {
29 idle = 0,
30 ok,
31 busy,
32 wrong_password,
33 busy_progressbar,
34 password_matrix_ready,
35 no_user_password_unlock, // FIXME: translate on receive to command status error (fix in firmware?)
36 smartcard_error,
37 security_bit_active
38 };
39 const int CMD_START_VALUE = 0x20;
40 const int CMD_END_VALUE = 0x60;
41 }
42 namespace stick10 {
43 enum class command_status : uint8_t {
44 ok = 0,
45 wrong_CRC,
46 wrong_slot,
47 slot_not_programmed,
48 wrong_password = 4,
49 not_authorized,
50 timestamp_warning,
51 no_name_error,
52 not_supported,
53 unknown_command,
54 AES_dec_failed
55 };
56 enum class device_status : uint8_t {
57 ok = 0,
58 busy = 1,
59 error,
60 received_report,
61 };
62 }
63
64
65 enum class CommandID : uint8_t {
66 GET_STATUS = 0x00,
67 WRITE_TO_SLOT = 0x01,
68 READ_SLOT_NAME = 0x02,
69 READ_SLOT = 0x03,
70 GET_CODE = 0x04,
71 WRITE_CONFIG = 0x05,
72 ERASE_SLOT = 0x06,
73 FIRST_AUTHENTICATE = 0x07,
74 AUTHORIZE = 0x08,
75 GET_PASSWORD_RETRY_COUNT = 0x09,
76 CLEAR_WARNING = 0x0A,
77 SET_TIME = 0x0B,
78 TEST_COUNTER = 0x0C,
79 TEST_TIME = 0x0D,
80 USER_AUTHENTICATE = 0x0E,
81 GET_USER_PASSWORD_RETRY_COUNT = 0x0F,
82 USER_AUTHORIZE = 0x10,
83 UNLOCK_USER_PASSWORD = 0x11,
84 LOCK_DEVICE = 0x12,
85 FACTORY_RESET = 0x13,
86 CHANGE_USER_PIN = 0x14,
87 CHANGE_ADMIN_PIN = 0x15,
88 WRITE_TO_SLOT_2 = 0x16,
89 SEND_OTP_DATA = 0x17,
90
91 ENABLE_CRYPTED_PARI = 0x20,
92 DISABLE_CRYPTED_PARI = 0x20 + 1,
93 ENABLE_HIDDEN_CRYPTED_PARI = 0x20 + 2,
94 DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3,
95 ENABLE_FIRMWARE_UPDATE = 0x20 + 4, //enables update mode
96 EXPORT_FIRMWARE_TO_FILE = 0x20 + 5,
97 GENERATE_NEW_KEYS = 0x20 + 6,
98 FILL_SD_CARD_WITH_RANDOM_CHARS = 0x20 + 7,
99
100 WRITE_STATUS_DATA = 0x20 + 8, //@unused
101 ENABLE_READONLY_UNCRYPTED_LUN = 0x20 + 9,
102 ENABLE_READWRITE_UNCRYPTED_LUN = 0x20 + 10,
103
104 SEND_PASSWORD_MATRIX = 0x20 + 11, //@unused
105 SEND_PASSWORD_MATRIX_PINDATA = 0x20 + 12, //@unused
106 SEND_PASSWORD_MATRIX_SETUP = 0x20 + 13, //@unused
107
108 GET_DEVICE_STATUS = 0x20 + 14,
109 SEND_DEVICE_STATUS = 0x20 + 15,
110
111 SEND_HIDDEN_VOLUME_PASSWORD = 0x20 + 16, //@unused
112 SEND_HIDDEN_VOLUME_SETUP = 0x20 + 17,
113 SEND_PASSWORD = 0x20 + 18,
114 SEND_NEW_PASSWORD = 0x20 + 19,
115 CLEAR_NEW_SD_CARD_FOUND = 0x20 + 20,
116
117 SEND_STARTUP = 0x20 + 21,
118 SEND_CLEAR_STICK_KEYS_NOT_INITIATED = 0x20 + 22,
119 SEND_LOCK_STICK_HARDWARE = 0x20 + 23, //locks firmware upgrade
120
121 PRODUCTION_TEST = 0x20 + 24,
122 SEND_DEBUG_DATA = 0x20 + 25, //@unused
123
124 CHANGE_UPDATE_PIN = 0x20 + 26,
125
126 GET_PW_SAFE_SLOT_STATUS = 0x60,
127 GET_PW_SAFE_SLOT_NAME = 0x61,
128 GET_PW_SAFE_SLOT_PASSWORD = 0x62,
129 GET_PW_SAFE_SLOT_LOGINNAME = 0x63,
130 SET_PW_SAFE_SLOT_DATA_1 = 0x64,
131 SET_PW_SAFE_SLOT_DATA_2 = 0x65,
132 PW_SAFE_ERASE_SLOT = 0x66,
133 PW_SAFE_ENABLE = 0x67,
134 PW_SAFE_INIT_KEY = 0x68, //@unused
135 PW_SAFE_SEND_DATA = 0x69, //@unused
136 SD_CARD_HIGH_WATERMARK = 0x70,
137 DETECT_SC_AES = 0x6a,
138 NEW_AES_KEY = 0x6b
139 };
140
141 const char *commandid_to_string(CommandID id);
142 }
143 }
144 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef CXX_SEMANTICS_H
22 #define CXX_SEMANTICS_H
23
24 #ifndef _MSC_VER
25 #define __packed __attribute__((__packed__))
26 #else
27 #define __packed
28 #endif
29
30 #ifdef _MSC_VER
31 #define strdup _strdup
32 #endif
33
34 /*
35 * There's no need to include Boost for a simple subset this project needs.
36 */
37 namespace semantics {
38 class non_constructible {
39 non_constructible() {}
40 };
41 }
42
43 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef DEVICE_H
22 #define DEVICE_H
23 #include <chrono>
24 #include "hidapi/hidapi.h"
25 #include <cstdint>
26 #include <string>
27
28 #define HID_REPORT_SIZE 65
29
30 #include <atomic>
31
32 namespace nitrokey {
33 namespace device {
34 using namespace std::chrono_literals;
35 using std::chrono::milliseconds;
36
37 struct EnumClassHash
38 {
39 template <typename T>
40 std::size_t operator()(T t) const
41 {
42 return static_cast<std::size_t>(t);
43 }
44 };
45
46 enum class DeviceModel{
47 PRO,
48 STORAGE
49 };
50
51 #include <atomic>
52
53 class Device {
54
55 public:
56
57 struct ErrorCounters{
58 using cnt = std::atomic_int;
59 cnt wrong_CRC;
60 cnt CRC_other_than_awaited;
61 cnt busy;
62 cnt total_retries;
63 cnt sending_error;
64 cnt receiving_error;
65 cnt total_comm_runs;
66 cnt successful_storage_commands;
67 cnt command_successful_recv;
68 cnt recv_executed;
69 cnt sends_executed;
70 cnt busy_progressbar;
71 cnt command_result_not_equal_0_recv;
72 cnt communication_successful;
73 cnt low_level_reconnect;
74 std::string get_as_string();
75
76 } m_counters = {};
77
78
79 Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
80 const milliseconds send_receive_delay, const int retry_receiving_count,
81 const milliseconds retry_timeout);
82
83 virtual ~Device();
84
85 // lack of device is not actually an error,
86 // so it doesn't throw
87 virtual bool connect();
88 virtual bool disconnect();
89
90 /*
91 * Sends packet of HID_REPORT_SIZE.
92 */
93 virtual int send(const void *packet);
94
95 /*
96 * Gets packet of HID_REPORT_SIZE.
97 * Can sleep. See below.
98 */
99 virtual int recv(void *packet);
100
101 /***
102 * Returns true if some device is visible by OS with given VID and PID
103 * whether the device is connected through HID API or not.
104 * @return true if visible by OS
105 */
106 bool could_be_enumerated();
107
108 void show_stats();
109 // ErrorCounters get_stats(){ return m_counters; }
110 int get_retry_receiving_count() const { return m_retry_receiving_count; };
111 int get_retry_sending_count() const { return m_retry_sending_count; };
112 std::chrono::milliseconds get_retry_timeout() const { return m_retry_timeout; };
113 std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;}
114
115 int get_last_command_status() {int a = std::atomic_exchange(&last_command_status, static_cast<uint8_t>(0)); return a;};
116 void set_last_command_status(uint8_t _err) { last_command_status = _err;} ;
117 bool last_command_sucessfull() const {return last_command_status == 0;};
118 DeviceModel get_device_model() const {return m_model;}
119 void set_receiving_delay(std::chrono::milliseconds delay);
120 void set_retry_delay(std::chrono::milliseconds delay);
121 static void set_default_device_speed(int delay);
122 void setDefaultDelay();
123
124 private:
125 std::atomic<uint8_t> last_command_status;
126 void _reconnect();
127 bool _connect();
128 bool _disconnect();
129
130 protected:
131 const uint16_t m_vid;
132 const uint16_t m_pid;
133 const DeviceModel m_model;
134
135 /*
136 * While the project uses Signal11 portable HIDAPI
137 * library, there's no way of doing it asynchronously,
138 * hence polling.
139 */
140 const int m_retry_sending_count;
141 const int m_retry_receiving_count;
142 std::chrono::milliseconds m_retry_timeout;
143 std::chrono::milliseconds m_send_receive_delay;
144 std::atomic<hid_device *>mp_devhandle;
145
146 static std::atomic_int instances_count;
147 static std::chrono::milliseconds default_delay ;
148 };
149
150 class Stick10 : public Device {
151 public:
152 Stick10();
153
154 };
155
156 class Stick20 : public Device {
157 public:
158 Stick20();
159 };
160 }
161 }
162 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef DEVICE_PROTO_H
22 #define DEVICE_PROTO_H
23
24 #include <utility>
25 #include <thread>
26 #include <type_traits>
27 #include <stdexcept>
28 #include <string>
29 // a local version for compatibility with Windows
30 #include <stdint.h>
31 #include "cxx_semantics.h"
32 #include "device.h"
33 #include "misc.h"
34 #include "log.h"
35 #include "command_id.h"
36 #include "dissect.h"
37 #include "CommandFailedException.h"
38 #include "LongOperationInProgressException.h"
39
40 #define STICK20_UPDATE_MODE_VID 0x03EB
41 #define STICK20_UPDATE_MODE_PID 0x2FF1
42
43 #define PAYLOAD_SIZE 53
44 #define PWS_SLOT_COUNT 16
45 #define PWS_SLOTNAME_LENGTH 11
46 #define PWS_PASSWORD_LENGTH 20
47 #define PWS_LOGINNAME_LENGTH 32
48
49 #define PWS_SEND_PASSWORD 0
50 #define PWS_SEND_LOGINNAME 1
51 #define PWS_SEND_TAB 2
52 #define PWS_SEND_CR 3
53
54 #include <mutex>
55 #include "DeviceCommunicationExceptions.h"
56 #define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
57
58 namespace nitrokey {
59 namespace proto {
60 extern std::mutex send_receive_mtx;
61
62
63 /*
64 * POD types for HID proto commands
65 * Instances are meant to be __packed.
66 *
67 * TODO (future) support for Big Endian
68 */
69 #pragma pack (push,1)
70 /*
71 * Every packet is a USB HID report (check USB spec)
72 */
73 template<CommandID cmd_id, typename Payload>
74 struct HIDReport {
75 uint8_t _zero;
76 CommandID command_id; // uint8_t
77 union {
78 uint8_t _padding[HID_REPORT_SIZE - 6];
79 Payload payload;
80 } __packed;
81 uint32_t crc;
82
83 // POD types can't have non-default constructors
84 // used in Transaction<>::run()
85 void initialize() {
86 bzero(this, sizeof *this);
87 command_id = cmd_id;
88 }
89
90 uint32_t calculate_CRC() const {
91 // w/o leading zero, a part of each HID packet
92 // w/o 4-byte crc
93 return misc::stm_crc32((const uint8_t *) (this) + 1,
94 (size_t) (HID_REPORT_SIZE - 5));
95 }
96
97 void update_CRC() { crc = calculate_CRC(); }
98
99 bool isCRCcorrect() const { return crc == calculate_CRC(); }
100
101 bool isValid() const {
102 return true;
103 // return !_zero && payload.isValid() && isCRCcorrect();
104 }
105
106 operator std::string() const {
107 // Packet type is known upfront in normal operation.
108 // Can't be used to dissect random packets.
109 return QueryDissector<cmd_id, decltype(*this)>::dissect(*this);
110 }
111 } __packed;
112
113 /*
114 * Response payload (the parametrized type inside struct HIDReport)
115 *
116 * command_id member in incoming HIDReport structure carries the command
117 * type last used.
118 */
119 namespace DeviceResponseConstants{
120 //magic numbers from firmware
121 static constexpr auto storage_status_absolute_address = 21;
122 static constexpr auto storage_data_absolute_address = storage_status_absolute_address + 5;
123 static constexpr auto header_size = 8; //from _zero to last_command_status inclusive
124 static constexpr auto footer_size = 4; //crc
125 static constexpr auto wrapping_size = header_size + footer_size;
126 }
127
128 template<CommandID cmd_id, typename ResponsePayload>
129 struct DeviceResponse {
130 static constexpr auto storage_status_padding_size =
131 DeviceResponseConstants::storage_status_absolute_address - DeviceResponseConstants::header_size;
132
133 uint8_t _zero;
134 uint8_t device_status;
135 uint8_t command_id; // originally last_command_type
136 uint32_t last_command_crc;
137 uint8_t last_command_status;
138
139 union {
140 uint8_t _padding[HID_REPORT_SIZE - DeviceResponseConstants::wrapping_size];
141 ResponsePayload payload;
142 struct {
143 uint8_t _storage_status_padding[storage_status_padding_size];
144 uint8_t command_counter;
145 uint8_t command_id;
146 uint8_t device_status; //@see stick20::device_status
147 uint8_t progress_bar_value;
148 } __packed storage_status;
149 } __packed;
150
151 uint32_t crc;
152
153 void initialize() { bzero(this, sizeof *this); }
154
155 uint32_t calculate_CRC() const {
156 // w/o leading zero, a part of each HID packet
157 // w/o 4-byte crc
158 return misc::stm_crc32((const uint8_t *) (this) + 1,
159 (size_t) (HID_REPORT_SIZE - 5));
160 }
161
162 void update_CRC() { crc = calculate_CRC(); }
163 bool isCRCcorrect() const { return crc == calculate_CRC(); }
164 bool isValid() const {
165 // return !_zero && payload.isValid() && isCRCcorrect() &&
166 // command_id == (uint8_t)(cmd_id);
167 return crc != 0;
168 }
169
170 operator std::string() const {
171 return ResponseDissector<cmd_id, decltype(*this)>::dissect(*this);
172 }
173 } __packed;
174
175 struct EmptyPayload {
176 bool isValid() const { return true; }
177
178 std::string dissect() const { return std::string("Empty Payload."); }
179 } __packed;
180
181 template<typename command_packet, typename response_payload>
182 class ClearingProxy {
183 public:
184 ClearingProxy(command_packet &p) {
185 packet = p;
186 bzero(&p, sizeof(p));
187 }
188
189 ~ClearingProxy() {
190 bzero(&packet, sizeof(packet));
191 }
192
193 response_payload &data() {
194 return packet.payload;
195 }
196
197 command_packet packet;
198 };
199
200 template<CommandID cmd_id, typename command_payload, typename response_payload>
201 class Transaction : semantics::non_constructible {
202 public:
203 // Types declared in command class scope can't be reached from there.
204 typedef command_payload CommandPayload;
205 typedef response_payload ResponsePayload;
206
207
208 typedef struct HIDReport<cmd_id, CommandPayload> OutgoingPacket;
209 typedef struct DeviceResponse<cmd_id, ResponsePayload> ResponsePacket;
210 #pragma pack (pop)
211
212 static_assert(std::is_pod<OutgoingPacket>::value,
213 "outgoingpacket must be a pod type");
214 static_assert(std::is_pod<ResponsePacket>::value,
215 "ResponsePacket must be a POD type");
216 static_assert(sizeof(OutgoingPacket) == HID_REPORT_SIZE,
217 "OutgoingPacket type is not the right size");
218 static_assert(sizeof(ResponsePacket) == HID_REPORT_SIZE,
219 "ResponsePacket type is not the right size");
220
221 static uint32_t getCRC(
222 const command_payload &payload) {
223 OutgoingPacket outp;
224 outp.initialize();
225 outp.payload = payload;
226 outp.update_CRC();
227 return outp.crc;
228 }
229
230 template<typename T>
231 static void clear_packet(T &st) {
232 bzero(&st, sizeof(st));
233 }
234
235 static ClearingProxy<ResponsePacket, response_payload> run(std::shared_ptr<device::Device> dev,
236 const command_payload &payload) {
237 using namespace ::nitrokey::device;
238 using namespace ::nitrokey::log;
239 using namespace std::chrono_literals;
240
241 std::lock_guard<std::mutex> guard(send_receive_mtx);
242
243 LOG(__FUNCTION__, Loglevel::DEBUG_L2);
244
245 if (dev == nullptr){
246 LOG(std::string("Throw: Device not initialized"), Loglevel::DEBUG_L1);
247 throw DeviceNotConnected("Device not initialized");
248 }
249 dev->m_counters.total_comm_runs++;
250
251 int status;
252 OutgoingPacket outp;
253 ResponsePacket resp;
254
255 // POD types can't have non-default constructors
256 outp.initialize();
257 resp.initialize();
258
259 outp.payload = payload;
260 outp.update_CRC();
261
262 LOG("-------------------", Loglevel::DEBUG);
263 LOG("Outgoing HID packet:", Loglevel::DEBUG);
264 LOG(static_cast<std::string>(outp), Loglevel::DEBUG);
265 LOG(std::string("=> ") + std::string(commandid_to_string(static_cast<CommandID>(outp.command_id))), Loglevel::DEBUG_L1);
266
267
268 if (!outp.isValid()) {
269 LOG(std::string("Throw: Invalid outgoing packet"), Loglevel::DEBUG_L1);
270 throw DeviceSendingFailure("Invalid outgoing packet");
271 }
272
273 bool successful_communication = false;
274 int receiving_retry_counter = 0;
275 int sending_retry_counter = dev->get_retry_sending_count();
276 while (sending_retry_counter-- > 0) {
277 dev->m_counters.sends_executed++;
278 status = dev->send(&outp);
279 if (status <= 0){
280 //FIXME early disconnection not yet working properly
281 // LOG("Encountered communication error, disconnecting device", Loglevel::DEBUG_L2);
282 // dev->disconnect();
283 dev->m_counters.sending_error++;
284 LOG(std::string("Throw: Device error while sending command "), Loglevel::DEBUG_L1);
285 throw DeviceSendingFailure(
286 std::string("Device error while sending command ") +
287 std::to_string(status));
288 }
289
290 std::this_thread::sleep_for(dev->get_send_receive_delay());
291
292 // FIXME make checks done in device:recv here
293 receiving_retry_counter = dev->get_retry_receiving_count();
294 int busy_counter = 0;
295 auto retry_timeout = dev->get_retry_timeout();
296 while (receiving_retry_counter-- > 0) {
297 dev->m_counters.recv_executed++;
298 status = dev->recv(&resp);
299
300 if (dev->get_device_model() == DeviceModel::STORAGE &&
301 resp.command_id >= stick20::CMD_START_VALUE &&
302 resp.command_id < stick20::CMD_END_VALUE ) {
303 LOG(std::string("Detected storage device cmd, status: ") +
304 std::to_string(resp.storage_status.device_status), Loglevel::DEBUG_L2);
305
306 resp.last_command_status = static_cast<uint8_t>(stick10::command_status::ok);
307 switch (static_cast<stick20::device_status>(resp.storage_status.device_status)) {
308 case stick20::device_status::idle :
309 case stick20::device_status::ok:
310 resp.device_status = static_cast<uint8_t>(stick10::device_status::ok);
311 break;
312 case stick20::device_status::busy:
313 case stick20::device_status::busy_progressbar: //TODO this will be modified later for getting progressbar status
314 resp.device_status = static_cast<uint8_t>(stick10::device_status::busy);
315 break;
316 case stick20::device_status::wrong_password:
317 resp.last_command_status = static_cast<uint8_t>(stick10::command_status::wrong_password);
318 resp.device_status = static_cast<uint8_t>(stick10::device_status::ok);
319 break;
320 case stick20::device_status::no_user_password_unlock:
321 resp.last_command_status = static_cast<uint8_t>(stick10::command_status::AES_dec_failed);
322 resp.device_status = static_cast<uint8_t>(stick10::device_status::ok);
323 default:
324 LOG(std::string("Unknown storage device status, cannot translate: ") +
325 std::to_string(resp.storage_status.device_status), Loglevel::DEBUG);
326 resp.device_status = resp.storage_status.device_status;
327 break;
328 };
329 }
330
331 //Some of the commands return wrong CRC, for now skip checking it (TODO list and report)
332 //if (resp.device_status == 0 && resp.last_command_crc == outp.crc && resp.isCRCcorrect()) break;
333 auto CRC_equal_awaited = true; // resp.last_command_crc == outp.crc;
334 if (resp.device_status == static_cast<uint8_t>(stick10::device_status::ok) &&
335 CRC_equal_awaited && resp.isValid()){
336 successful_communication = true;
337 break;
338 }
339 if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy)) {
340 dev->m_counters.busy++;
341
342 if (busy_counter++<10) {
343 receiving_retry_counter++;
344 LOG("Status busy, not decreasing receiving_retry_counter counter: " +
345 std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2);
346 } else {
347 retry_timeout *= 2;
348 retry_timeout = std::min(retry_timeout, 300ms);
349 busy_counter = 0;
350 LOG("Status busy, decreasing receiving_retry_counter counter: " +
351 std::to_string(receiving_retry_counter) + ", current delay:"
352 + std::to_string(retry_timeout.count()), Loglevel::DEBUG);
353 LOG(std::string("Busy retry ")
354 + std::to_string(resp.storage_status.device_status)
355 + " "
356 + std::to_string(retry_timeout.count())
357 + " "
358 + std::to_string(receiving_retry_counter)
359 , Loglevel::DEBUG_L1);
360 }
361 }
362 if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy) &&
363 static_cast<stick20::device_status>(resp.storage_status.device_status)
364 == stick20::device_status::busy_progressbar){
365 successful_communication = true;
366 break;
367 }
368 LOG(std::string("Retry status - dev status, awaited cmd crc, correct packet CRC: ")
369 + std::to_string(resp.device_status) +
370 " " + std::to_string(CRC_equal_awaited) +
371 " " + std::to_string(resp.isCRCcorrect()), Loglevel::DEBUG_L2);
372
373 if (!resp.isCRCcorrect()) dev->m_counters.wrong_CRC++;
374 if (!CRC_equal_awaited) dev->m_counters.CRC_other_than_awaited++;
375
376
377 LOG(
378 "Device is not ready or received packet's last CRC is not equal to sent CRC packet, retrying...",
379 Loglevel::DEBUG_L2);
380 LOG("Invalid incoming HID packet:", Loglevel::DEBUG_L2);
381 LOG(static_cast<std::string>(resp), Loglevel::DEBUG_L2);
382 dev->m_counters.total_retries++;
383 LOG(".", Loglevel::DEBUG_L1);
384 std::this_thread::sleep_for(retry_timeout);
385 continue;
386 }
387 if (successful_communication) break;
388 LOG(std::string("Resending (outer loop) "), Loglevel::DEBUG_L2);
389 LOG(std::string("sending_retry_counter count: ") + std::to_string(sending_retry_counter),
390 Loglevel::DEBUG);
391 }
392
393 if(resp.last_command_crc != outp.crc){
394 LOG(std::string("Accepting response with CRC other than expected ")
395 + "Command ID: " + std::to_string(resp.command_id) + " " +
396 commandid_to_string(static_cast<CommandID>(resp.command_id)) + " "
397 + "Reported by response and expected: " + std::to_string(resp.last_command_crc) + "!=" + std::to_string(outp.crc),
398 Loglevel::WARNING
399 );
400 }
401
402 dev->set_last_command_status(resp.last_command_status); // FIXME should be handled on device.recv
403
404 clear_packet(outp);
405
406
407 if (status <= 0) {
408 dev->m_counters.receiving_error++;
409 LOG(std::string("Throw: Device error while executing command "), Loglevel::DEBUG_L1);
410 throw DeviceReceivingFailure( //FIXME replace with CriticalErrorException
411 std::string("Device error while executing command ") +
412 std::to_string(status));
413 }
414
415 LOG(std::string("<= ") +
416 std::string(
417 commandid_to_string(static_cast<CommandID>(resp.command_id))
418 + std::string(" ")
419 + std::to_string(resp.device_status)
420 + std::string(" ")
421 + std::to_string(resp.storage_status.device_status)
422 // + std::to_string( status_translate_command(resp.storage_status.device_status))
423 ), Loglevel::DEBUG_L1);
424
425 LOG("Incoming HID packet:", Loglevel::DEBUG);
426 LOG(static_cast<std::string>(resp), Loglevel::DEBUG);
427 if (dev->get_retry_receiving_count() - receiving_retry_counter > 2) {
428 LOG(std::string("Packet received with receiving_retry_counter count: ") +
429 std::to_string(receiving_retry_counter),
430 Loglevel::DEBUG_L1);
431 }
432
433 if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy) &&
434 static_cast<stick20::device_status>(resp.storage_status.device_status)
435 == stick20::device_status::busy_progressbar){
436 dev->m_counters.busy_progressbar++;
437 LOG(std::string("Throw: Long operation in progress exception"), Loglevel::DEBUG_L1);
438 throw LongOperationInProgressException(
439 resp.command_id, resp.device_status, resp.storage_status.progress_bar_value);
440 }
441
442 if (!resp.isValid()) {
443 LOG(std::string("Throw: Invalid incoming packet"), Loglevel::DEBUG_L1);
444 throw InvalidCRCReceived("Invalid incoming packet");
445 }
446 if (receiving_retry_counter <= 0){
447 LOG(std::string("Throw: \"Maximum receiving_retry_counter count reached for receiving response from the device!\""
448 + std::to_string(receiving_retry_counter)), Loglevel::DEBUG_L1);
449 throw DeviceReceivingFailure(
450 "Maximum receiving_retry_counter count reached for receiving response from the device!");
451 }
452 dev->m_counters.communication_successful++;
453
454 if (resp.last_command_status != static_cast<uint8_t>(stick10::command_status::ok)){
455 dev->m_counters.command_result_not_equal_0_recv++;
456 LOG(std::string("Throw: CommandFailedException"), Loglevel::DEBUG_L1);
457 throw CommandFailedException(resp.command_id, resp.last_command_status);
458 }
459
460 dev->m_counters.command_successful_recv++;
461
462 if (dev->get_device_model() == DeviceModel::STORAGE &&
463 resp.command_id >= stick20::CMD_START_VALUE &&
464 resp.command_id < stick20::CMD_END_VALUE ) {
465 dev->m_counters.successful_storage_commands++;
466 }
467
468 if (!resp.isCRCcorrect())
469 LOG(std::string("Accepting response from device with invalid CRC. ")
470 + "Command ID: " + std::to_string(resp.command_id) + " " +
471 commandid_to_string(static_cast<CommandID>(resp.command_id)) + " "
472 + "Reported and calculated: " + std::to_string(resp.crc) + "!=" + std::to_string(resp.calculate_CRC()),
473 Loglevel::WARNING
474 );
475
476 // See: DeviceResponse
477 return resp;
478 }
479
480 static ClearingProxy<ResponsePacket, response_payload> run(std::shared_ptr<device::Device> dev) {
481 command_payload empty_payload;
482 return run(dev, empty_payload);
483 }
484 };
485 }
486 }
487 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 /*
22 * Protocol packet dissection
23 */
24 #ifndef DISSECT_H
25 #define DISSECT_H
26 #include <string>
27 #include <sstream>
28 #include <iomanip>
29 #include "misc.h"
30 #include "cxx_semantics.h"
31 #include "command_id.h"
32 #include "device_proto.h"
33
34 namespace nitrokey {
35 namespace proto {
36
37 template <CommandID id, class HIDPacket>
38 class QueryDissector : semantics::non_constructible {
39 public:
40 static std::string dissect(const HIDPacket &pod) {
41 std::stringstream out;
42
43 #ifdef LOG_VOLATILE_DATA
44 out << "Raw HID packet:" << std::endl;
45 out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod);
46 #endif
47
48 out << "Contents:" << std::endl;
49 out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id))
50 << std::endl;
51 out << "CRC:\t"
52 << std::hex << std::setw(2) << std::setfill('0')
53 << pod.crc << std::endl;
54
55 out << "Payload:" << std::endl;
56 out << pod.payload.dissect();
57 return out.str();
58 }
59 };
60
61
62
63
64 template <CommandID id, class HIDPacket>
65 class ResponseDissector : semantics::non_constructible {
66 public:
67 static std::string status_translate_device(int status){
68 auto enum_status = static_cast<proto::stick10::device_status>(status);
69 switch (enum_status){
70 case stick10::device_status::ok: return "OK";
71 case stick10::device_status::busy: return "BUSY";
72 case stick10::device_status::error: return "ERROR";
73 case stick10::device_status::received_report: return "RECEIVED_REPORT";
74 }
75 return std::string("UNKNOWN: ") + std::to_string(status);
76 }
77
78 static std::string to_upper(std::string str){
79 for (auto & c: str) c = toupper(c);
80 return str;
81 }
82 static std::string status_translate_command(int status){
83 auto enum_status = static_cast<proto::stick10::command_status >(status);
84 switch (enum_status) {
85 #define p(X) case X: return to_upper(std::string(#X));
86 p(stick10::command_status::ok)
87 p(stick10::command_status::wrong_CRC)
88 p(stick10::command_status::wrong_slot)
89 p(stick10::command_status::slot_not_programmed)
90 p(stick10::command_status::wrong_password)
91 p(stick10::command_status::not_authorized)
92 p(stick10::command_status::timestamp_warning)
93 p(stick10::command_status::no_name_error)
94 p(stick10::command_status::not_supported)
95 p(stick10::command_status::unknown_command)
96 p(stick10::command_status::AES_dec_failed)
97 #undef p
98 }
99 return std::string("UNKNOWN: ") + std::to_string(status);
100 }
101
102 static std::string dissect(const HIDPacket &pod) {
103 std::stringstream out;
104
105 // FIXME use values from firmware (possibly generate separate
106 // header automatically)
107
108 #ifdef LOG_VOLATILE_DATA
109 out << "Raw HID packet:" << std::endl;
110 out << ::nitrokey::misc::hexdump((const uint8_t *)(&pod), sizeof pod);
111 #endif
112
113 out << "Device status:\t" << pod.device_status + 0 << " "
114 << status_translate_device(pod.device_status) << std::endl;
115 out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) << " hex: " << std::hex << (int)pod.command_id
116 << std::endl;
117 out << "Last command CRC:\t"
118 << std::hex << std::setw(2) << std::setfill('0')
119 << pod.last_command_crc << std::endl;
120 out << "Last command status:\t" << pod.last_command_status + 0 << " "
121 << status_translate_command(pod.last_command_status) << std::endl;
122 out << "CRC:\t"
123 << std::hex << std::setw(2) << std::setfill('0')
124 << pod.crc << std::endl;
125 if((int)pod.command_id == pod.storage_status.command_id){
126 out << "Storage stick status (where applicable):" << std::endl;
127 #define d(x) out << " "#x": \t"<< std::hex << std::setw(2) \
128 << std::setfill('0')<< static_cast<int>(x) << std::endl;
129 d(pod.storage_status.command_counter);
130 d(pod.storage_status.command_id);
131 d(pod.storage_status.device_status);
132 d(pod.storage_status.progress_bar_value);
133 #undef d
134 }
135
136 out << "Payload:" << std::endl;
137 out << pod.payload.dissect();
138 return out.str();
139 }
140 };
141 }
142 }
143
144 #endif
0 /*******************************************************
1 HIDAPI - Multi-Platform library for
2 communication with HID devices.
3
4 Alan Ott
5 Signal 11 Software
6
7 8/22/2009
8
9 Copyright 2009, All Rights Reserved.
10
11 At the discretion of the user of this library,
12 this software may be licensed under the terms of the
13 GNU General Public License v3, a BSD-Style license, or the
14 original HIDAPI license as outlined in the LICENSE.txt,
15 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
16 files located at the root of the source distribution.
17 These files may also be found in the public source
18 code repository located at:
19 http://github.com/signal11/hidapi .
20 ********************************************************/
21
22 /** @file
23 * @defgroup API hidapi API
24 */
25
26 #ifndef HIDAPI_H__
27 #define HIDAPI_H__
28
29 #include <wchar.h>
30
31 #ifdef _WIN32
32 #define HID_API_EXPORT __declspec(dllexport)
33 #define HID_API_CALL
34 #else
35 #define HID_API_EXPORT /**< API export macro */
36 #define HID_API_CALL /**< API call macro */
37 #endif
38
39 #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
40
41 #ifdef __cplusplus
42 extern "C" {
43 #endif
44 struct hid_device_;
45 typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
46
47 /** hidapi info structure */
48 struct hid_device_info {
49 /** Platform-specific device path */
50 char *path;
51 /** Device Vendor ID */
52 unsigned short vendor_id;
53 /** Device Product ID */
54 unsigned short product_id;
55 /** Serial Number */
56 wchar_t *serial_number;
57 /** Device Release Number in binary-coded decimal,
58 also known as Device Version Number */
59 unsigned short release_number;
60 /** Manufacturer String */
61 wchar_t *manufacturer_string;
62 /** Product string */
63 wchar_t *product_string;
64 /** Usage Page for this Device/Interface
65 (Windows/Mac only). */
66 unsigned short usage_page;
67 /** Usage for this Device/Interface
68 (Windows/Mac only).*/
69 unsigned short usage;
70 /** The USB interface which this logical device
71 represents. Valid on both Linux implementations
72 in all cases, and valid on the Windows implementation
73 only if the device contains more than one interface. */
74 int interface_number;
75
76 /** Pointer to the next device */
77 struct hid_device_info *next;
78 };
79
80
81 /** @brief Initialize the HIDAPI library.
82
83 This function initializes the HIDAPI library. Calling it is not
84 strictly necessary, as it will be called automatically by
85 hid_enumerate() and any of the hid_open_*() functions if it is
86 needed. This function should be called at the beginning of
87 execution however, if there is a chance of HIDAPI handles
88 being opened by different threads simultaneously.
89
90 @ingroup API
91
92 @returns
93 This function returns 0 on success and -1 on error.
94 */
95 int HID_API_EXPORT HID_API_CALL hid_init(void);
96
97 /** @brief Finalize the HIDAPI library.
98
99 This function frees all of the static data associated with
100 HIDAPI. It should be called at the end of execution to avoid
101 memory leaks.
102
103 @ingroup API
104
105 @returns
106 This function returns 0 on success and -1 on error.
107 */
108 int HID_API_EXPORT HID_API_CALL hid_exit(void);
109
110 /** @brief Enumerate the HID Devices.
111
112 This function returns a linked list of all the HID devices
113 attached to the system which match vendor_id and product_id.
114 If @p vendor_id is set to 0 then any vendor matches.
115 If @p product_id is set to 0 then any product matches.
116 If @p vendor_id and @p product_id are both set to 0, then
117 all HID devices will be returned.
118
119 @ingroup API
120 @param vendor_id The Vendor ID (VID) of the types of device
121 to open.
122 @param product_id The Product ID (PID) of the types of
123 device to open.
124
125 @returns
126 This function returns a pointer to a linked list of type
127 struct #hid_device, containing information about the HID devices
128 attached to the system, or NULL in the case of failure. Free
129 this linked list by calling hid_free_enumeration().
130 */
131 struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
132
133 /** @brief Free an enumeration Linked List
134
135 This function frees a linked list created by hid_enumerate().
136
137 @ingroup API
138 @param devs Pointer to a list of struct_device returned from
139 hid_enumerate().
140 */
141 void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
142
143 /** @brief Open a HID device using a Vendor ID (VID), Product ID
144 (PID) and optionally a serial number.
145
146 If @p serial_number is NULL, the first device with the
147 specified VID and PID is opened.
148
149 @ingroup API
150 @param vendor_id The Vendor ID (VID) of the device to open.
151 @param product_id The Product ID (PID) of the device to open.
152 @param serial_number The Serial Number of the device to open
153 (Optionally NULL).
154
155 @returns
156 This function returns a pointer to a #hid_device object on
157 success or NULL on failure.
158 */
159 HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
160
161 /** @brief Open a HID device by its path name.
162
163 The path name be determined by calling hid_enumerate(), or a
164 platform-specific path name can be used (eg: /dev/hidraw0 on
165 Linux).
166
167 @ingroup API
168 @param path The path name of the device to open
169
170 @returns
171 This function returns a pointer to a #hid_device object on
172 success or NULL on failure.
173 */
174 HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
175
176 /** @brief Write an Output report to a HID device.
177
178 The first byte of @p data[] must contain the Report ID. For
179 devices which only support a single report, this must be set
180 to 0x0. The remaining bytes contain the report data. Since
181 the Report ID is mandatory, calls to hid_write() will always
182 contain one more byte than the report contains. For example,
183 if a hid report is 16 bytes long, 17 bytes must be passed to
184 hid_write(), the Report ID (or 0x0, for devices with a
185 single report), followed by the report data (16 bytes). In
186 this example, the length passed in would be 17.
187
188 hid_write() will send the data on the first OUT endpoint, if
189 one exists. If it does not, it will send the data through
190 the Control Endpoint (Endpoint 0).
191
192 @ingroup API
193 @param device A device handle returned from hid_open().
194 @param data The data to send, including the report number as
195 the first byte.
196 @param length The length in bytes of the data to send.
197
198 @returns
199 This function returns the actual number of bytes written and
200 -1 on error.
201 */
202 int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
203
204 /** @brief Read an Input report from a HID device with timeout.
205
206 Input reports are returned
207 to the host through the INTERRUPT IN endpoint. The first byte will
208 contain the Report number if the device uses numbered reports.
209
210 @ingroup API
211 @param device A device handle returned from hid_open().
212 @param data A buffer to put the read data into.
213 @param length The number of bytes to read. For devices with
214 multiple reports, make sure to read an extra byte for
215 the report number.
216 @param milliseconds timeout in milliseconds or -1 for blocking wait.
217
218 @returns
219 This function returns the actual number of bytes read and
220 -1 on error. If no packet was available to be read within
221 the timeout period, this function returns 0.
222 */
223 int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
224
225 /** @brief Read an Input report from a HID device.
226
227 Input reports are returned
228 to the host through the INTERRUPT IN endpoint. The first byte will
229 contain the Report number if the device uses numbered reports.
230
231 @ingroup API
232 @param device A device handle returned from hid_open().
233 @param data A buffer to put the read data into.
234 @param length The number of bytes to read. For devices with
235 multiple reports, make sure to read an extra byte for
236 the report number.
237
238 @returns
239 This function returns the actual number of bytes read and
240 -1 on error. If no packet was available to be read and
241 the handle is in non-blocking mode, this function returns 0.
242 */
243 int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
244
245 /** @brief Set the device handle to be non-blocking.
246
247 In non-blocking mode calls to hid_read() will return
248 immediately with a value of 0 if there is no data to be
249 read. In blocking mode, hid_read() will wait (block) until
250 there is data to read before returning.
251
252 Nonblocking can be turned on and off at any time.
253
254 @ingroup API
255 @param device A device handle returned from hid_open().
256 @param nonblock enable or not the nonblocking reads
257 - 1 to enable nonblocking
258 - 0 to disable nonblocking.
259
260 @returns
261 This function returns 0 on success and -1 on error.
262 */
263 int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
264
265 /** @brief Send a Feature report to the device.
266
267 Feature reports are sent over the Control endpoint as a
268 Set_Report transfer. The first byte of @p data[] must
269 contain the Report ID. For devices which only support a
270 single report, this must be set to 0x0. The remaining bytes
271 contain the report data. Since the Report ID is mandatory,
272 calls to hid_send_feature_report() will always contain one
273 more byte than the report contains. For example, if a hid
274 report is 16 bytes long, 17 bytes must be passed to
275 hid_send_feature_report(): the Report ID (or 0x0, for
276 devices which do not use numbered reports), followed by the
277 report data (16 bytes). In this example, the length passed
278 in would be 17.
279
280 @ingroup API
281 @param device A device handle returned from hid_open().
282 @param data The data to send, including the report number as
283 the first byte.
284 @param length The length in bytes of the data to send, including
285 the report number.
286
287 @returns
288 This function returns the actual number of bytes written and
289 -1 on error.
290 */
291 int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
292
293 /** @brief Get a feature report from a HID device.
294
295 Set the first byte of @p data[] to the Report ID of the
296 report to be read. Make sure to allow space for this
297 extra byte in @p data[]. Upon return, the first byte will
298 still contain the Report ID, and the report data will
299 start in data[1].
300
301 @ingroup API
302 @param device A device handle returned from hid_open().
303 @param data A buffer to put the read data into, including
304 the Report ID. Set the first byte of @p data[] to the
305 Report ID of the report to be read, or set it to zero
306 if your device does not use numbered reports.
307 @param length The number of bytes to read, including an
308 extra byte for the report ID. The buffer can be longer
309 than the actual report.
310
311 @returns
312 This function returns the number of bytes read plus
313 one for the report ID (which is still in the first
314 byte), or -1 on error.
315 */
316 int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
317
318 /** @brief Close a HID device.
319
320 @ingroup API
321 @param device A device handle returned from hid_open().
322 */
323 void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
324
325 /** @brief Get The Manufacturer String from a HID device.
326
327 @ingroup API
328 @param device A device handle returned from hid_open().
329 @param string A wide string buffer to put the data into.
330 @param maxlen The length of the buffer in multiples of wchar_t.
331
332 @returns
333 This function returns 0 on success and -1 on error.
334 */
335 int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
336
337 /** @brief Get The Product String from a HID device.
338
339 @ingroup API
340 @param device A device handle returned from hid_open().
341 @param string A wide string buffer to put the data into.
342 @param maxlen The length of the buffer in multiples of wchar_t.
343
344 @returns
345 This function returns 0 on success and -1 on error.
346 */
347 int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
348
349 /** @brief Get The Serial Number String from a HID device.
350
351 @ingroup API
352 @param device A device handle returned from hid_open().
353 @param string A wide string buffer to put the data into.
354 @param maxlen The length of the buffer in multiples of wchar_t.
355
356 @returns
357 This function returns 0 on success and -1 on error.
358 */
359 int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
360
361 /** @brief Get a string from a HID device, based on its string index.
362
363 @ingroup API
364 @param device A device handle returned from hid_open().
365 @param string_index The index of the string to get.
366 @param string A wide string buffer to put the data into.
367 @param maxlen The length of the buffer in multiples of wchar_t.
368
369 @returns
370 This function returns 0 on success and -1 on error.
371 */
372 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
373
374 /** @brief Get a string describing the last error which occurred.
375
376 @ingroup API
377 @param device A device handle returned from hid_open().
378
379 @returns
380 This function returns a string containing the last error
381 which occurred or NULL if none has occurred.
382 */
383 HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
384
385 #ifdef __cplusplus
386 }
387 #endif
388
389 #endif
390
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LOG_H
22 #define LOG_H
23
24 #include <string>
25 #include <cstddef>
26
27 #include <functional>
28
29 namespace nitrokey {
30 namespace log {
31
32 //for MSVC
33 #ifdef ERROR
34 #undef ERROR
35 #endif
36
37
38 enum class Loglevel : int {
39 ERROR,
40 WARNING,
41 INFO,
42 DEBUG_L1,
43 DEBUG,
44 DEBUG_L2
45 };
46
47 class LogHandler {
48 public:
49 virtual void print(const std::string &, Loglevel lvl) = 0;
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);
59 };
60
61 class FunctionalLogHandler : public LogHandler {
62 using log_function_type = std::function<void(std::string)>;
63 log_function_type log_function;
64 public:
65 FunctionalLogHandler(log_function_type _log_function);
66 virtual void print(const std::string &, Loglevel lvl);
67
68 };
69
70 extern StdlogHandler stdlog_handler;
71
72 class Log {
73 public:
74 Log() : mp_loghandler(&stdlog_handler), m_loglevel(Loglevel::WARNING) {}
75
76 static Log &instance() {
77 if (mp_instance == nullptr) mp_instance = new Log;
78 return *mp_instance;
79 }
80
81 void operator()(const std::string &, Loglevel);
82 void set_loglevel(Loglevel lvl) { m_loglevel = lvl; }
83 void set_handler(LogHandler *handler) { mp_loghandler = handler; }
84
85 private:
86 LogHandler *mp_loghandler;
87 Loglevel m_loglevel;
88
89 static Log *mp_instance;
90 };
91 }
92 }
93
94
95 #ifdef NO_LOG
96 #define LOG(string, level) while(false){}
97 #define LOGD(string) while(false){}
98 #else
99 #define LOG(string, level) nitrokey::log::Log::instance()((string), (level))
100 #define LOGD(string) nitrokey::log::Log::instance()((string), (nitrokey::log::Loglevel::DEBUG_L2))
101 #endif
102
103 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21
22 #ifndef MISC_H
23 #define MISC_H
24 #include <stdio.h>
25 #include <string>
26 #include <vector>
27 #include <string.h>
28 #include "log.h"
29 #include "LibraryException.h"
30 #include <sstream>
31 #include <iomanip>
32
33
34 namespace nitrokey {
35 namespace misc {
36
37 template<typename T>
38 std::string toHex(T value){
39 using namespace std;
40 std::ostringstream oss;
41 oss << std::hex << std::setw(sizeof(value)*2) << std::setfill('0') << value;
42 return oss.str();
43 }
44
45 /**
46 * Copies string from pointer to fixed size C-style array. Src needs to be a valid C-string - eg. ended with '\0'.
47 * Throws when source is bigger than destination.
48 * @tparam T type of destination array
49 * @param dest fixed size destination array
50 * @param src pointer to source c-style valid string
51 */
52 template <typename T>
53 void strcpyT(T& dest, const char* src){
54
55 if (src == nullptr)
56 // throw EmptySourceStringException(slot_number);
57 return;
58 const size_t s_dest = sizeof dest;
59 LOG(std::string("strcpyT sizes dest src ")
60 +std::to_string(s_dest)+ " "
61 +std::to_string(strlen(src))+ " "
62 ,nitrokey::log::Loglevel::DEBUG_L2);
63 if (strlen(src) > s_dest){
64 throw TooLongStringException(strlen(src), s_dest, src);
65 }
66 strncpy((char*) &dest, src, s_dest);
67 }
68
69 #define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
70 template <typename T>
71 typename T::CommandPayload get_payload(){
72 //Create, initialize and return by value command payload
73 typename T::CommandPayload st;
74 bzero(&st, sizeof(st));
75 return st;
76 }
77
78 template<typename CMDTYPE, typename Tdev>
79 void execute_password_command(Tdev &stick, const char *password) {
80 auto p = get_payload<CMDTYPE>();
81 p.set_defaults();
82 strcpyT(p.password, password);
83 CMDTYPE::CommandTransaction::run(stick, p);
84 }
85
86 std::string hexdump(const uint8_t *p, size_t size, bool print_header=true, bool print_ascii=true,
87 bool print_empty=true);
88 uint32_t stm_crc32(const uint8_t *data, size_t size);
89 std::vector<uint8_t> hex_string_to_byte(const char* hexString);
90 }
91 }
92
93 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef STICK10_COMMANDS_H
22 #define STICK10_COMMANDS_H
23
24 #include <bitset>
25 #include <iomanip>
26 #include <string>
27 #include <sstream>
28 #include <stdint.h>
29 #include "device_proto.h"
30 #include "command.h"
31
32 #pragma pack (push,1)
33
34 namespace nitrokey {
35 namespace proto {
36
37
38
39 /*
40 * Stick10 protocol definition
41 */
42 namespace stick10 {
43 class GetSlotName : public Command<CommandID::READ_SLOT_NAME> {
44 public:
45 // reachable as a typedef in Transaction
46 struct CommandPayload {
47 uint8_t slot_number;
48
49 bool isValid() const { return slot_number<0x10+3; }
50 std::string dissect() const {
51 std::stringstream ss;
52 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
53 return ss.str();
54 }
55 } __packed;
56
57 struct ResponsePayload {
58 uint8_t slot_name[15];
59
60 bool isValid() const { return true; }
61 std::string dissect() const {
62 std::stringstream ss;
63 print_to_ss_volatile(slot_name);
64 return ss.str();
65 }
66 } __packed;
67
68 typedef Transaction<command_id(), struct CommandPayload,
69 struct ResponsePayload> CommandTransaction;
70 };
71
72 class EraseSlot : Command<CommandID::ERASE_SLOT> {
73 public:
74 struct CommandPayload {
75 uint8_t slot_number;
76
77 bool isValid() const { return !(slot_number & 0xF0); }
78 std::string dissect() const {
79 std::stringstream ss;
80 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
81 return ss.str();
82 }
83 } __packed;
84
85 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
86 CommandTransaction;
87 };
88
89 class SetTime : Command<CommandID::SET_TIME> {
90 public:
91 struct CommandPayload {
92 uint8_t reset; // 0 - get time, 1 - set time
93 uint64_t time; // posix time
94
95 bool isValid() const { return reset && reset != 1; }
96 std::string dissect() const {
97 std::stringstream ss;
98 ss << "reset:\t" << (int)(reset) << std::endl;
99 ss << "time:\t" << (time) << std::endl;
100 return ss.str();
101 }
102 } __packed;
103
104 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
105 CommandTransaction;
106 };
107
108
109 class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
110 public:
111 struct CommandPayload {
112 uint8_t slot_number;
113 uint8_t slot_name[15];
114 uint8_t slot_secret[20];
115 union{
116 uint8_t _slot_config;
117 struct{
118 bool use_8_digits : 1;
119 bool use_enter : 1;
120 bool use_tokenID : 1;
121 };
122 };
123 union{
124 uint8_t slot_token_id[13]; /** OATH Token Identifier */
125 struct{ /** @see https://openauthentication.org/token-specs/ */
126 uint8_t omp[2];
127 uint8_t tt[2];
128 uint8_t mui[8];
129 uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
130 } slot_token_fields;
131 };
132 union{
133 uint64_t slot_counter;
134 uint8_t slot_counter_s[8];
135 } __packed;
136
137 bool isValid() const { return !(slot_number & 0xF0); }
138 std::string dissect() const {
139 std::stringstream ss;
140 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
141 print_to_ss_volatile(slot_name);
142 print_to_ss_volatile(slot_secret);
143 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
144 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
145 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
146 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
147
148 ss << "slot_token_id:\t";
149 for (auto i : slot_token_id)
150 ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ;
151 ss << std::endl;
152 ss << "slot_counter:\t[" << (int)slot_counter << "]\t"
153 << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false);
154
155 return ss.str();
156 }
157 } __packed;
158
159 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
160 CommandTransaction;
161 };
162
163 class WriteToTOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
164 public:
165 struct CommandPayload {
166 uint8_t slot_number;
167 uint8_t slot_name[15];
168 uint8_t slot_secret[20];
169 union{
170 uint8_t _slot_config;
171 struct{
172 bool use_8_digits : 1;
173 bool use_enter : 1;
174 bool use_tokenID : 1;
175 };
176 };
177 union{
178 uint8_t slot_token_id[13]; /** OATH Token Identifier */
179 struct{ /** @see https://openauthentication.org/token-specs/ */
180 uint8_t omp[2];
181 uint8_t tt[2];
182 uint8_t mui[8];
183 uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
184 } slot_token_fields;
185 };
186 uint16_t slot_interval;
187
188 bool isValid() const { return !(slot_number & 0xF0); } //TODO check
189 std::string dissect() const {
190 std::stringstream ss;
191 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
192 print_to_ss_volatile(slot_name);
193 print_to_ss_volatile(slot_secret);
194 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
195 ss << "slot_token_id:\t";
196 for (auto i : slot_token_id)
197 ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ;
198 ss << std::endl;
199 ss << "slot_interval:\t" << (int)slot_interval << std::endl;
200 return ss.str();
201 }
202 } __packed;
203
204 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
205 CommandTransaction;
206 };
207
208 class GetTOTP : Command<CommandID::GET_CODE> {
209 public:
210 struct CommandPayload {
211 uint8_t slot_number;
212 uint64_t challenge;
213 uint64_t last_totp_time;
214 uint8_t last_interval;
215
216 bool isValid() const { return !(slot_number & 0xF0); }
217 std::string dissect() const {
218 std::stringstream ss;
219 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
220 ss << "challenge:\t" << (challenge) << std::endl;
221 ss << "last_totp_time:\t" << (last_totp_time) << std::endl;
222 ss << "last_interval:\t" << (int)(last_interval) << std::endl;
223 return ss.str();
224 }
225 } __packed;
226
227 struct ResponsePayload {
228 union {
229 uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
230 struct {
231 uint32_t code;
232 union{
233 uint8_t _slot_config;
234 struct{
235 bool use_8_digits : 1;
236 bool use_enter : 1;
237 bool use_tokenID : 1;
238 };
239 };
240 } __packed ;
241 } __packed ;
242
243 bool isValid() const { return true; }
244 std::string dissect() const {
245 std::stringstream ss;
246 ss << "code:\t" << (code) << std::endl;
247 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
248 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
249 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
250 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
251 return ss.str();
252 }
253 } __packed;
254
255 typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload>
256 CommandTransaction;
257 };
258
259 class GetHOTP : Command<CommandID::GET_CODE> {
260 public:
261 struct CommandPayload {
262 uint8_t slot_number;
263
264 bool isValid() const { return (slot_number & 0xF0); }
265 std::string dissect() const {
266 std::stringstream ss;
267 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
268 return ss.str();
269 }
270 } __packed;
271
272 struct ResponsePayload {
273 union {
274 uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
275 struct {
276 uint32_t code;
277 union{
278 uint8_t _slot_config;
279 struct{
280 bool use_8_digits : 1;
281 bool use_enter : 1;
282 bool use_tokenID : 1;
283 };
284 };
285 } __packed;
286 } __packed;
287
288 bool isValid() const { return true; }
289 std::string dissect() const {
290 std::stringstream ss;
291 ss << "code:\t" << (code) << std::endl;
292 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
293 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
294 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
295 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
296 return ss.str();
297 }
298 } __packed;
299
300 typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload>
301 CommandTransaction;
302 };
303
304 class ReadSlot : Command<CommandID::READ_SLOT> {
305 public:
306 struct CommandPayload {
307 uint8_t slot_number;
308
309 bool isValid() const { return !(slot_number & 0xF0); }
310
311 std::string dissect() const {
312 std::stringstream ss;
313 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
314 return ss.str();
315 }
316 } __packed;
317
318 struct ResponsePayload {
319 uint8_t slot_name[15];
320 union{
321 uint8_t _slot_config;
322 struct{
323 bool use_8_digits : 1;
324 bool use_enter : 1;
325 bool use_tokenID : 1;
326 };
327 };
328 union{
329 uint8_t slot_token_id[13]; /** OATH Token Identifier */
330 struct{ /** @see https://openauthentication.org/token-specs/ */
331 uint8_t omp[2];
332 uint8_t tt[2];
333 uint8_t mui[8];
334 uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
335 } slot_token_fields;
336 };
337 union{
338 uint64_t slot_counter;
339 uint8_t slot_counter_s[8];
340 } __packed;
341
342 bool isValid() const { return true; }
343
344 std::string dissect() const {
345 std::stringstream ss;
346 print_to_ss_volatile(slot_name);
347 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
348 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
349 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
350 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
351
352 ss << "slot_token_id:\t";
353 for (auto i : slot_token_id)
354 ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ;
355 ss << std::endl;
356 ss << "slot_counter:\t[" << (int)slot_counter << "]\t"
357 << ::nitrokey::misc::hexdump((const uint8_t *)(&slot_counter), sizeof slot_counter, false);
358 return ss.str();
359 }
360 } __packed;
361
362 typedef Transaction<command_id(), struct CommandPayload,
363 struct ResponsePayload> CommandTransaction;
364 };
365
366 class GetStatus : Command<CommandID::GET_STATUS> {
367 public:
368 struct ResponsePayload {
369 uint16_t firmware_version;
370 union{
371 uint8_t card_serial[4];
372 uint32_t card_serial_u32;
373 } __packed;
374 union {
375 uint8_t general_config[5];
376 struct{
377 uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */
378 uint8_t capslock; /** same as numlock */
379 uint8_t scrolllock; /** same as numlock */
380 uint8_t enable_user_password;
381 uint8_t delete_user_password;
382 } __packed;
383 } __packed;
384 bool isValid() const { return enable_user_password!=delete_user_password; }
385
386 std::string get_card_serial_hex() const {
387 return nitrokey::misc::toHex(card_serial_u32);
388 }
389
390 std::string dissect() const {
391 std::stringstream ss;
392 ss << "firmware_version:\t"
393 << "[" << firmware_version << "]" << "\t"
394 << ::nitrokey::misc::hexdump(
395 (const uint8_t *)(&firmware_version), sizeof firmware_version, false);
396 ss << "card_serial_u32:\t" << std::hex << card_serial_u32 << std::endl;
397 ss << "card_serial:\t"
398 << ::nitrokey::misc::hexdump((const uint8_t *)(card_serial),
399 sizeof card_serial, false);
400 ss << "general_config:\t"
401 << ::nitrokey::misc::hexdump((const uint8_t *)(general_config),
402 sizeof general_config, false);
403 ss << "numlock:\t" << (int)numlock << std::endl;
404 ss << "capslock:\t" << (int)capslock << std::endl;
405 ss << "scrolllock:\t" << (int)scrolllock << std::endl;
406 ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl;
407 ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl;
408
409 return ss.str();
410 }
411 } __packed;
412
413 typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
414 CommandTransaction;
415 };
416
417 class GetPasswordRetryCount : Command<CommandID::GET_PASSWORD_RETRY_COUNT> {
418 public:
419 struct ResponsePayload {
420 uint8_t password_retry_count;
421
422 bool isValid() const { return true; }
423 std::string dissect() const {
424 std::stringstream ss;
425 ss << " password_retry_count\t" << (int)password_retry_count << std::endl;
426 return ss.str();
427 }
428 } __packed;
429
430 typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
431 CommandTransaction;
432 };
433
434 class GetUserPasswordRetryCount
435 : Command<CommandID::GET_USER_PASSWORD_RETRY_COUNT> {
436 public:
437 struct ResponsePayload {
438 uint8_t password_retry_count;
439
440 bool isValid() const { return true; }
441 std::string dissect() const {
442 std::stringstream ss;
443 ss << " password_retry_count\t" << (int)password_retry_count << std::endl;
444 return ss.str();
445 }
446 } __packed;
447
448 typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
449 CommandTransaction;
450 };
451
452 template <typename T, typename Q, int N>
453 void write_array(T &ss, Q (&arr)[N]){
454 for (int i=0; i<N; i++){
455 ss << std::hex << std::setfill('0') << std::setw(2) << (int)arr[i] << " ";
456 }
457 ss << std::endl;
458 };
459
460
461 class GetPasswordSafeSlotStatus : Command<CommandID::GET_PW_SAFE_SLOT_STATUS> {
462 public:
463 struct ResponsePayload {
464 uint8_t password_safe_status[PWS_SLOT_COUNT];
465
466 bool isValid() const { return true; }
467 std::string dissect() const {
468 std::stringstream ss;
469 ss << "password_safe_status\t";
470 write_array(ss, password_safe_status);
471 return ss.str();
472 }
473 } __packed;
474
475 typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
476 CommandTransaction;
477 };
478
479 class GetPasswordSafeSlotName : Command<CommandID::GET_PW_SAFE_SLOT_NAME> {
480 public:
481 struct CommandPayload {
482 uint8_t slot_number;
483
484 bool isValid() const { return !(slot_number & 0xF0); }
485 std::string dissect() const {
486 std::stringstream ss;
487 ss << "slot_number\t" << (int)slot_number << std::endl;
488 return ss.str();
489 }
490 } __packed;
491
492 struct ResponsePayload {
493 uint8_t slot_name[PWS_SLOTNAME_LENGTH];
494
495 bool isValid() const { return true; }
496 std::string dissect() const {
497 std::stringstream ss;
498 print_to_ss_volatile(slot_name);
499 return ss.str();
500 }
501 } __packed;
502
503 typedef Transaction<command_id(), struct CommandPayload,
504 struct ResponsePayload> CommandTransaction;
505 };
506
507 class GetPasswordSafeSlotPassword
508 : Command<CommandID::GET_PW_SAFE_SLOT_PASSWORD> {
509 public:
510 struct CommandPayload {
511 uint8_t slot_number;
512
513 bool isValid() const { return !(slot_number & 0xF0); }
514 std::string dissect() const {
515 std::stringstream ss;
516 ss << " slot_number\t" << (int)slot_number << std::endl;
517 return ss.str();
518 }
519 } __packed;
520
521 struct ResponsePayload {
522 uint8_t slot_password[PWS_PASSWORD_LENGTH];
523
524 bool isValid() const { return true; }
525 std::string dissect() const {
526 std::stringstream ss;
527 print_to_ss_volatile(slot_password);
528 return ss.str();
529 }
530 } __packed;
531
532 typedef Transaction<command_id(), struct CommandPayload,
533 struct ResponsePayload> CommandTransaction;
534 };
535
536 class GetPasswordSafeSlotLogin
537 : Command<CommandID::GET_PW_SAFE_SLOT_LOGINNAME> {
538 public:
539 struct CommandPayload {
540 uint8_t slot_number;
541
542 bool isValid() const { return !(slot_number & 0xF0); }
543 std::string dissect() const {
544 std::stringstream ss;
545 ss << " slot_number\t" << (int)slot_number << std::endl;
546 return ss.str();
547 }
548 } __packed;
549
550 struct ResponsePayload {
551 uint8_t slot_login[PWS_LOGINNAME_LENGTH];
552
553 bool isValid() const { return true; }
554 std::string dissect() const {
555 std::stringstream ss;
556 print_to_ss_volatile(slot_login);
557 return ss.str();
558 }
559 } __packed;
560
561 typedef Transaction<command_id(), struct CommandPayload,
562 struct ResponsePayload> CommandTransaction;
563 };
564
565 class SetPasswordSafeSlotData : Command<CommandID::SET_PW_SAFE_SLOT_DATA_1> {
566 public:
567 struct CommandPayload {
568 uint8_t slot_number;
569 uint8_t slot_name[PWS_SLOTNAME_LENGTH];
570 uint8_t slot_password[PWS_PASSWORD_LENGTH];
571
572 bool isValid() const { return !(slot_number & 0xF0); }
573 std::string dissect() const {
574 std::stringstream ss;
575 ss << " slot_number\t" << (int)slot_number << std::endl;
576 print_to_ss_volatile(slot_name);
577 print_to_ss_volatile(slot_password);
578 return ss.str();
579 }
580 } __packed;
581
582 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
583 CommandTransaction;
584 };
585
586 class SetPasswordSafeSlotData2 : Command<CommandID::SET_PW_SAFE_SLOT_DATA_2> {
587 public:
588 struct CommandPayload {
589 uint8_t slot_number;
590 uint8_t slot_login_name[PWS_LOGINNAME_LENGTH];
591
592 bool isValid() const { return !(slot_number & 0xF0); }
593 std::string dissect() const {
594 std::stringstream ss;
595 ss << " slot_number\t" << (int)slot_number << std::endl;
596 print_to_ss_volatile(slot_login_name);
597 return ss.str();
598 }
599 } __packed;
600
601 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
602 CommandTransaction;
603 };
604
605 class ErasePasswordSafeSlot : Command<CommandID::PW_SAFE_ERASE_SLOT> {
606 public:
607 struct CommandPayload {
608 uint8_t slot_number;
609
610 bool isValid() const { return !(slot_number & 0xF0); }
611 std::string dissect() const {
612 std::stringstream ss;
613 ss << " slot_number\t" << (int)slot_number << std::endl;
614 return ss.str();
615 }
616
617 } __packed;
618
619 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
620 CommandTransaction;
621 };
622
623 class EnablePasswordSafe : Command<CommandID::PW_SAFE_ENABLE> {
624 public:
625 struct CommandPayload {
626 uint8_t user_password[30];
627
628 bool isValid() const { return true; }
629 std::string dissect() const {
630 std::stringstream ss;
631 print_to_ss_volatile(user_password);
632 return ss.str();
633 }
634 } __packed;
635
636 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
637 CommandTransaction;
638 };
639
640 class PasswordSafeInitKey : Command<CommandID::PW_SAFE_INIT_KEY> {
641 /**
642 * never used in Nitrokey App
643 */
644 public:
645 typedef Transaction<command_id(), struct EmptyPayload, struct EmptyPayload>
646 CommandTransaction;
647 };
648
649 class PasswordSafeSendSlotViaHID : Command<CommandID::PW_SAFE_SEND_DATA> {
650 /**
651 * never used in Nitrokey App
652 */
653 public:
654 struct CommandPayload {
655 uint8_t slot_number;
656 uint8_t slot_kind;
657
658 bool isValid() const { return !(slot_number & 0xF0); }
659 } __packed;
660
661 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
662 CommandTransaction;
663 };
664
665 // TODO "Device::passwordSafeSendSlotDataViaHID"
666
667 class WriteGeneralConfig : Command<CommandID::WRITE_CONFIG> {
668 public:
669 struct CommandPayload {
670 union{
671 uint8_t config[5];
672 struct{
673 uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */
674 uint8_t capslock; /** same as numlock */
675 uint8_t scrolllock; /** same as numlock */
676 uint8_t enable_user_password;
677 uint8_t delete_user_password;
678 };
679 };
680 std::string dissect() const {
681 std::stringstream ss;
682 ss << "numlock:\t" << (int)numlock << std::endl;
683 ss << "capslock:\t" << (int)capslock << std::endl;
684 ss << "scrolllock:\t" << (int)scrolllock << std::endl;
685 ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl;
686 ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl;
687 return ss.str();
688 }
689 } __packed;
690
691 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
692 CommandTransaction;
693 };
694
695 class FirstAuthenticate : Command<CommandID::FIRST_AUTHENTICATE> {
696 public:
697 struct CommandPayload {
698 uint8_t card_password[25];
699 uint8_t temporary_password[25];
700
701 bool isValid() const { return true; }
702
703 std::string dissect() const {
704 std::stringstream ss;
705 print_to_ss_volatile(card_password);
706 hexdump_to_ss(temporary_password);
707 return ss.str();
708 }
709 } __packed;
710
711 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
712 CommandTransaction;
713 };
714
715 class UserAuthenticate : Command<CommandID::USER_AUTHENTICATE> {
716 public:
717 struct CommandPayload {
718 uint8_t card_password[25];
719 uint8_t temporary_password[25];
720
721 bool isValid() const { return true; }
722 std::string dissect() const {
723 std::stringstream ss;
724 print_to_ss_volatile(card_password);
725 hexdump_to_ss(temporary_password);
726 return ss.str();
727 }
728 } __packed;
729
730 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
731 CommandTransaction;
732 };
733
734 class Authorize : Command<CommandID::AUTHORIZE> {
735 public:
736 struct CommandPayload {
737 uint32_t crc_to_authorize;
738 uint8_t temporary_password[25];
739
740 std::string dissect() const {
741 std::stringstream ss;
742 ss << " crc_to_authorize:\t" << std::hex << std::setw(2) << std::setfill('0') << crc_to_authorize<< std::endl;
743 hexdump_to_ss(temporary_password);
744 return ss.str();
745 }
746 } __packed;
747
748 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
749 CommandTransaction;
750 };
751
752 class UserAuthorize : Command<CommandID::USER_AUTHORIZE> {
753 public:
754 struct CommandPayload {
755 uint32_t crc_to_authorize;
756 uint8_t temporary_password[25];
757 std::string dissect() const {
758 std::stringstream ss;
759 ss << " crc_to_authorize:\t" << crc_to_authorize<< std::endl;
760 hexdump_to_ss(temporary_password);
761 return ss.str();
762 }
763 } __packed;
764
765 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
766 CommandTransaction;
767 };
768
769 class UnlockUserPassword : Command<CommandID::UNLOCK_USER_PASSWORD> {
770 public:
771 struct CommandPayload {
772 uint8_t admin_password[25];
773 uint8_t user_new_password[25];
774 std::string dissect() const {
775 std::stringstream ss;
776 print_to_ss_volatile(admin_password);
777 print_to_ss_volatile(user_new_password);
778 return ss.str();
779 }
780 } __packed;
781
782 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
783 CommandTransaction;
784 };
785
786 class ChangeUserPin : Command<CommandID::CHANGE_USER_PIN> {
787 public:
788 struct CommandPayload {
789 uint8_t old_pin[25];
790 uint8_t new_pin[25];
791 std::string dissect() const {
792 std::stringstream ss;
793 print_to_ss_volatile(old_pin);
794 print_to_ss_volatile(new_pin);
795 return ss.str();
796 }
797 } __packed;
798
799 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
800 CommandTransaction;
801 };
802
803 class IsAESSupported : Command<CommandID::DETECT_SC_AES> {
804 public:
805 struct CommandPayload {
806 uint8_t user_password[20];
807 std::string dissect() const {
808 std::stringstream ss;
809 print_to_ss_volatile(user_password);
810 return ss.str();
811 }
812 } __packed;
813
814 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
815 CommandTransaction;
816 };
817
818
819 class ChangeAdminPin : Command<CommandID::CHANGE_ADMIN_PIN> {
820 public:
821 struct CommandPayload {
822 uint8_t old_pin[25];
823 uint8_t new_pin[25];
824 std::string dissect() const {
825 std::stringstream ss;
826 print_to_ss_volatile(old_pin);
827 print_to_ss_volatile(new_pin);
828 return ss.str();
829 }
830 } __packed;
831
832 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
833 CommandTransaction;
834 };
835
836 class LockDevice : Command<CommandID::LOCK_DEVICE> {
837 public:
838 typedef Transaction<command_id(), struct EmptyPayload, struct EmptyPayload>
839 CommandTransaction;
840 };
841
842 class FactoryReset : Command<CommandID::FACTORY_RESET> {
843 public:
844 struct CommandPayload {
845 uint8_t admin_password[20];
846 std::string dissect() const {
847 std::stringstream ss;
848 print_to_ss_volatile(admin_password);
849 return ss.str();
850 }
851 } __packed;
852
853 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
854 CommandTransaction;
855 };
856
857 class BuildAESKey : Command<CommandID::NEW_AES_KEY> {
858 public:
859 struct CommandPayload {
860 uint8_t admin_password[20];
861 std::string dissect() const {
862 std::stringstream ss;
863 print_to_ss_volatile(admin_password);
864 return ss.str();
865 }
866 } __packed;
867
868 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
869 CommandTransaction;
870
871 };
872
873 }
874 }
875 }
876 #pragma pack (pop)
877 #endif
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21
22 #ifndef LIBNITROKEY_STICK10_COMMANDS_0_8_H
23 #define LIBNITROKEY_STICK10_COMMANDS_0_8_H
24
25 #include <bitset>
26 #include <iomanip>
27 #include <string>
28 #include <sstream>
29 #include <cstdint>
30 #include "command.h"
31 #include "device_proto.h"
32 #include "stick10_commands.h"
33
34 #pragma pack (push,1)
35
36
37 namespace nitrokey {
38 namespace proto {
39
40 /*
41 * Stick10 protocol definition
42 */
43 namespace stick10_08 {
44 using stick10::FirstAuthenticate;
45 using stick10::UserAuthenticate;
46 using stick10::SetTime;
47 using stick10::GetStatus;
48 using stick10::BuildAESKey;
49 using stick10::ChangeAdminPin;
50 using stick10::ChangeUserPin;
51 using stick10::EnablePasswordSafe;
52 using stick10::ErasePasswordSafeSlot;
53 using stick10::FactoryReset;
54 using stick10::GetPasswordRetryCount;
55 using stick10::GetUserPasswordRetryCount;
56 using stick10::GetPasswordSafeSlotLogin;
57 using stick10::GetPasswordSafeSlotName;
58 using stick10::GetPasswordSafeSlotPassword;
59 using stick10::GetPasswordSafeSlotStatus;
60 using stick10::GetSlotName;
61 using stick10::IsAESSupported;
62 using stick10::LockDevice;
63 using stick10::PasswordSafeInitKey;
64 using stick10::PasswordSafeSendSlotViaHID;
65 using stick10::SetPasswordSafeSlotData;
66 using stick10::SetPasswordSafeSlotData2;
67 using stick10::UnlockUserPassword;
68 using stick10::ReadSlot;
69
70 class EraseSlot : Command<CommandID::ERASE_SLOT> {
71 public:
72 struct CommandPayload {
73 uint8_t slot_number;
74 uint8_t temporary_admin_password[25];
75
76 bool isValid() const { return !(slot_number & 0xF0); }
77 std::string dissect() const {
78 std::stringstream ss;
79 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
80 hexdump_to_ss(temporary_admin_password);
81 return ss.str();
82 }
83 } __packed;
84
85 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
86 CommandTransaction;
87 };
88
89 class SendOTPData : Command<CommandID::SEND_OTP_DATA> {
90 //admin auth
91 public:
92 struct CommandPayload {
93 uint8_t temporary_admin_password[25];
94 uint8_t type; //S-secret, N-name
95 uint8_t id; //multiple reports for values longer than 30 bytes
96 uint8_t data[30]; //data, does not need null termination
97
98 bool isValid() const { return true; }
99
100 void setTypeName(){
101 type = 'N';
102 }
103 void setTypeSecret(){
104 type = 'S';
105 }
106
107 std::string dissect() const {
108 std::stringstream ss;
109 hexdump_to_ss(temporary_admin_password);
110 ss << "type:\t" << type << std::endl;
111 ss << "id:\t" << (int)id << std::endl;
112 #ifdef LOG_VOLATILE_DATA
113 ss << "data:" << std::endl
114 << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data);
115 #else
116 ss << " Volatile data not logged" << std::endl;
117 #endif
118 return ss.str();
119 }
120 } __packed;
121
122
123 struct ResponsePayload {
124 union {
125 uint8_t data[40];
126 } __packed;
127
128 bool isValid() const { return true; }
129 std::string dissect() const {
130 std::stringstream ss;
131 #ifdef LOG_VOLATILE_DATA
132 ss << "data:" << std::endl
133 << ::nitrokey::misc::hexdump((const uint8_t *) (&data), sizeof data);
134 #else
135 ss << " Volatile data not logged" << std::endl;
136 #endif
137 return ss.str();
138 }
139 } __packed;
140
141
142 typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload>
143 CommandTransaction;
144 };
145
146 class WriteToOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
147 //admin auth
148 public:
149 struct CommandPayload {
150 uint8_t temporary_admin_password[25];
151 uint8_t slot_number;
152 union {
153 uint64_t slot_counter_or_interval;
154 uint8_t slot_counter_s[8];
155 } __packed;
156 union {
157 uint8_t _slot_config;
158 struct {
159 bool use_8_digits : 1;
160 bool use_enter : 1;
161 bool use_tokenID : 1;
162 };
163 };
164 union {
165 uint8_t slot_token_id[13]; /** OATH Token Identifier */
166 struct { /** @see https://openauthentication.org/token-specs/ */
167 uint8_t omp[2];
168 uint8_t tt[2];
169 uint8_t mui[8];
170 uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
171 } slot_token_fields;
172 };
173
174 bool isValid() const { return true; }
175
176 std::string dissect() const {
177 std::stringstream ss;
178 hexdump_to_ss(temporary_admin_password);
179 ss << "slot_config:\t" << std::bitset<8>((int) _slot_config) << std::endl;
180 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
181 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
182 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
183 ss << "slot_number:\t" << (int) (slot_number) << std::endl;
184 ss << "slot_counter_or_interval:\t[" << (int) slot_counter_or_interval << "]\t"
185 << ::nitrokey::misc::hexdump((const uint8_t *) (&slot_counter_or_interval), sizeof slot_counter_or_interval, false);
186
187 ss << "slot_token_id:\t";
188 for (auto i : slot_token_id)
189 ss << std::hex << std::setw(2) << std::setfill('0') << (int) i << " ";
190 ss << std::endl;
191
192 return ss.str();
193 }
194 } __packed;
195
196 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
197 CommandTransaction;
198 };
199
200 class GetHOTP : Command<CommandID::GET_CODE> {
201 public:
202 struct CommandPayload {
203 uint8_t slot_number;
204 struct {
205 uint64_t challenge; //@unused
206 uint64_t last_totp_time; //@unused
207 uint8_t last_interval; //@unused
208 } __packed _unused;
209 uint8_t temporary_user_password[25];
210
211 bool isValid() const { return (slot_number & 0xF0); }
212 std::string dissect() const {
213 std::stringstream ss;
214 hexdump_to_ss(temporary_user_password);
215 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
216 return ss.str();
217 }
218 } __packed;
219
220 struct ResponsePayload {
221 union {
222 uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
223 struct {
224 uint32_t code;
225 union{
226 uint8_t _slot_config;
227 struct{
228 bool use_8_digits : 1;
229 bool use_enter : 1;
230 bool use_tokenID : 1;
231 };
232 };
233 } __packed;
234 } __packed;
235
236 bool isValid() const { return true; }
237 std::string dissect() const {
238 std::stringstream ss;
239 ss << "code:\t" << (code) << std::endl;
240 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
241 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
242 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
243 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
244 return ss.str();
245 }
246 } __packed;
247
248 typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload>
249 CommandTransaction;
250 };
251
252
253 class GetTOTP : Command<CommandID::GET_CODE> {
254 //user auth
255 public:
256 struct CommandPayload {
257 uint8_t slot_number;
258 uint64_t challenge; //@unused
259 uint64_t last_totp_time; //@unused
260 uint8_t last_interval; //@unused
261 uint8_t temporary_user_password[25];
262
263 bool isValid() const { return !(slot_number & 0xF0); }
264 std::string dissect() const {
265 std::stringstream ss;
266 hexdump_to_ss(temporary_user_password);
267 ss << "slot_number:\t" << (int)(slot_number) << std::endl;
268 ss << "challenge:\t" << (challenge) << std::endl;
269 ss << "last_totp_time:\t" << (last_totp_time) << std::endl;
270 ss << "last_interval:\t" << (int)(last_interval) << std::endl;
271 return ss.str();
272 }
273 } __packed;
274
275 struct ResponsePayload {
276 union {
277 uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
278 struct {
279 uint32_t code;
280 union{
281 uint8_t _slot_config;
282 struct{
283 bool use_8_digits : 1;
284 bool use_enter : 1;
285 bool use_tokenID : 1;
286 };
287 };
288 } __packed ;
289 } __packed ;
290
291 bool isValid() const { return true; }
292 std::string dissect() const {
293 std::stringstream ss;
294 ss << "code:\t" << (code) << std::endl;
295 ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
296 ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
297 ss << "\tuse_enter(1):\t" << use_enter << std::endl;
298 ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
299 return ss.str();
300 }
301 } __packed;
302
303 typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload>
304 CommandTransaction;
305 };
306
307
308 class WriteGeneralConfig : Command<CommandID::WRITE_CONFIG> {
309 //admin auth
310 public:
311 struct CommandPayload {
312 union{
313 uint8_t config[5];
314 struct{
315 uint8_t numlock; /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */
316 uint8_t capslock; /** same as numlock */
317 uint8_t scrolllock; /** same as numlock */
318 uint8_t enable_user_password;
319 uint8_t delete_user_password;
320 };
321 };
322 uint8_t temporary_admin_password[25];
323
324 std::string dissect() const {
325 std::stringstream ss;
326 ss << "numlock:\t" << (int)numlock << std::endl;
327 ss << "capslock:\t" << (int)capslock << std::endl;
328 ss << "scrolllock:\t" << (int)scrolllock << std::endl;
329 ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl;
330 ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl;
331 return ss.str();
332 }
333 } __packed;
334
335 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
336 CommandTransaction;
337 };
338 }
339 }
340 }
341 #pragma pack (pop)
342
343 #endif //LIBNITROKEY_STICK10_COMMANDS_0_8_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef STICK20_COMMANDS_H
22 #define STICK20_COMMANDS_H
23
24
25
26 #include <cstdint>
27 #include "command.h"
28 #include <string>
29 #include <sstream>
30 #include "device_proto.h"
31
32 #pragma pack (push,1)
33
34 namespace nitrokey {
35 namespace proto {
36
37 /*
38 * STICK20 protocol command ids
39 * a superset (almost) of STICK10
40 */
41
42 namespace stick20 {
43
44 class ChangeAdminUserPin20Current :
45 public PasswordCommand<CommandID::SEND_PASSWORD, PasswordKind::Admin> {};
46 class ChangeAdminUserPin20New :
47 public PasswordCommand<CommandID::SEND_NEW_PASSWORD, PasswordKind::Admin> {};
48 class UnlockUserPin :
49 public PasswordCommand<CommandID::UNLOCK_USER_PASSWORD, PasswordKind::Admin> {};
50
51 class EnableEncryptedPartition : public PasswordCommand<CommandID::ENABLE_CRYPTED_PARI> {};
52 class EnableHiddenEncryptedPartition : public PasswordCommand<CommandID::ENABLE_HIDDEN_CRYPTED_PARI> {};
53
54 //FIXME the volume disabling commands do not need password
55 class DisableEncryptedPartition : public PasswordCommand<CommandID::DISABLE_CRYPTED_PARI> {};
56 class DisableHiddenEncryptedPartition : public PasswordCommand<CommandID::DISABLE_HIDDEN_CRYPTED_PARI> {};
57
58 class EnableFirmwareUpdate : public PasswordCommand<CommandID::ENABLE_FIRMWARE_UPDATE> {};
59
60 class ChangeUpdatePassword : Command<CommandID::CHANGE_UPDATE_PIN> {
61 public:
62 struct CommandPayload {
63 uint8_t __gap;
64 uint8_t current_update_password[20];
65 uint8_t __gap2;
66 uint8_t new_update_password[20];
67 std::string dissect() const {
68 std::stringstream ss;
69 print_to_ss_volatile( current_update_password );
70 print_to_ss_volatile( new_update_password );
71 return ss.str();
72 }
73 };
74
75 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
76 CommandTransaction;
77 };
78
79 class ExportFirmware : public PasswordCommand<CommandID::EXPORT_FIRMWARE_TO_FILE> {};
80
81 class CreateNewKeys :
82 public PasswordCommand<CommandID::GENERATE_NEW_KEYS, PasswordKind::AdminPrefixed, 30> {};
83
84
85 class FillSDCardWithRandomChars : Command<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS> {
86 public:
87 enum class ChosenVolumes : uint8_t {
88 all_volumes = 0,
89 encrypted_volume = 1
90 };
91
92 struct CommandPayload {
93 uint8_t volume_flag;
94 uint8_t kind;
95 uint8_t admin_pin[20];
96
97 std::string dissect() const {
98 std::stringstream ss;
99 print_to_ss( (int) volume_flag );
100 print_to_ss( kind );
101 print_to_ss_volatile(admin_pin);
102 return ss.str();
103 }
104 void set_kind_user() {
105 kind = (uint8_t) 'P';
106 }
107 void set_defaults(){
108 set_kind_user();
109 volume_flag = static_cast<uint8_t>(ChosenVolumes::encrypted_volume);
110 }
111
112 } __packed;
113
114 typedef Transaction<Command<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS>::command_id(),
115 struct CommandPayload, struct EmptyPayload>
116 CommandTransaction;
117 };
118
119 namespace StorageCommandResponsePayload{
120 using namespace DeviceResponseConstants;
121 static constexpr auto padding_size =
122 storage_data_absolute_address - header_size;
123 struct TransmissionData{
124 uint8_t _padding[padding_size];
125
126 uint8_t SendCounter_u8;
127 uint8_t SendDataType_u8;
128 uint8_t FollowBytesFlag_u8;
129 uint8_t SendSize_u8;
130
131 std::string dissect() const {
132 std::stringstream ss;
133 ss << "_padding:" << std::endl
134 << ::nitrokey::misc::hexdump((const uint8_t *) (_padding),
135 sizeof _padding);
136 print_to_ss((int) SendCounter_u8);
137 print_to_ss((int) SendDataType_u8);
138 print_to_ss((int) FollowBytesFlag_u8);
139 print_to_ss((int) SendSize_u8);
140 return ss.str();
141 }
142
143 } __packed;
144 }
145
146 namespace DeviceConfigurationResponsePacket{
147
148 struct ResponsePayload {
149 StorageCommandResponsePayload::TransmissionData transmission_data;
150
151 uint16_t MagicNumber_StickConfig_u16;
152 /**
153 * READ_WRITE_ACTIVE = ReadWriteFlagUncryptedVolume_u8 == 0;
154 */
155 uint8_t ReadWriteFlagUncryptedVolume_u8;
156 uint8_t ReadWriteFlagCryptedVolume_u8;
157
158 union{
159 uint8_t VersionInfo_au8[4];
160 struct {
161 uint8_t _reserved;
162 uint8_t minor;
163 uint8_t _reserved2;
164 uint8_t major;
165 } __packed versionInfo;
166 } __packed;
167
168 uint8_t ReadWriteFlagHiddenVolume_u8;
169 uint8_t FirmwareLocked_u8;
170
171 union{
172 uint8_t NewSDCardFound_u8;
173 struct {
174 bool NewCard :1;
175 uint8_t Counter :7;
176 } __packed NewSDCardFound_st;
177 } __packed;
178
179 /**
180 * SD card FILLED with random chars
181 */
182 uint8_t SDFillWithRandomChars_u8;
183 uint32_t ActiveSD_CardID_u32;
184 union{
185 uint8_t VolumeActiceFlag_u8;
186 struct {
187 bool unencrypted :1;
188 bool encrypted :1;
189 bool hidden :1;
190 } __packed VolumeActiceFlag_st;
191 } __packed;
192 uint8_t NewSmartCardFound_u8;
193 uint8_t UserPwRetryCount;
194 uint8_t AdminPwRetryCount;
195 uint32_t ActiveSmartCardID_u32;
196 uint8_t StickKeysNotInitiated;
197
198 bool isValid() const { return true; }
199
200 std::string dissect() const {
201 std::stringstream ss;
202
203 print_to_ss(transmission_data.dissect());
204 print_to_ss( MagicNumber_StickConfig_u16 );
205 print_to_ss((int) ReadWriteFlagUncryptedVolume_u8 );
206 print_to_ss((int) ReadWriteFlagCryptedVolume_u8 );
207 print_to_ss((int) ReadWriteFlagHiddenVolume_u8 );
208 print_to_ss((int) VersionInfo_au8[1] );
209 print_to_ss((int) VersionInfo_au8[3] );
210 print_to_ss((int) FirmwareLocked_u8 );
211 print_to_ss((int) NewSDCardFound_u8 );
212 print_to_ss((int) NewSDCardFound_st.NewCard );
213 print_to_ss((int) NewSDCardFound_st.Counter );
214 print_to_ss((int) SDFillWithRandomChars_u8 );
215 print_to_ss( ActiveSD_CardID_u32 );
216 print_to_ss((int) VolumeActiceFlag_u8 );
217 print_to_ss((int) VolumeActiceFlag_st.unencrypted );
218 print_to_ss((int) VolumeActiceFlag_st.encrypted );
219 print_to_ss((int) VolumeActiceFlag_st.hidden);
220 print_to_ss((int) NewSmartCardFound_u8 );
221 print_to_ss((int) UserPwRetryCount );
222 print_to_ss((int) AdminPwRetryCount );
223 print_to_ss( ActiveSmartCardID_u32 );
224 print_to_ss((int) StickKeysNotInitiated );
225
226 return ss.str();
227 }
228 } __packed;
229 }
230
231 class SendStartup : Command<CommandID::SEND_STARTUP> {
232 public:
233 struct CommandPayload {
234 uint64_t localtime; // POSIX seconds from epoch start, supports until year 2106
235 std::string dissect() const {
236 std::stringstream ss;
237 print_to_ss( localtime );
238 return ss.str();
239 }
240 void set_defaults(){
241 localtime =
242 std::chrono::duration_cast<std::chrono::seconds> (
243 std::chrono::system_clock::now().time_since_epoch()).count();
244 }
245 }__packed;
246
247 using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload;
248
249 typedef Transaction<command_id(), struct CommandPayload, ResponsePayload>
250 CommandTransaction;
251 };
252
253
254 // TODO fix original nomenclature
255 class SendSetReadonlyToUncryptedVolume : public PasswordCommand<CommandID::ENABLE_READONLY_UNCRYPTED_LUN> {};
256 class SendSetReadwriteToUncryptedVolume : public PasswordCommand<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN> {};
257 class SendClearNewSdCardFound : public PasswordCommand<CommandID::CLEAR_NEW_SD_CARD_FOUND> {};
258
259 class GetDeviceStatus : Command<CommandID::GET_DEVICE_STATUS> {
260 public:
261 using ResponsePayload = DeviceConfigurationResponsePacket::ResponsePayload;
262
263 typedef Transaction<command_id(), struct EmptyPayload, ResponsePayload>
264 CommandTransaction;
265 };
266
267 class GetSDCardOccupancy : Command<CommandID::SD_CARD_HIGH_WATERMARK> {
268 public:
269 struct ResponsePayload {
270 uint8_t WriteLevelMin;
271 uint8_t WriteLevelMax;
272 uint8_t ReadLevelMin;
273 uint8_t ReadLevelMax;
274 std::string dissect() const {
275 std::stringstream ss;
276 print_to_ss((int) WriteLevelMin);
277 print_to_ss((int) WriteLevelMax);
278 print_to_ss((int) ReadLevelMin);
279 print_to_ss((int) ReadLevelMax);
280 return ss.str();
281 }
282 } __packed;
283
284 typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
285 CommandTransaction;
286 };
287
288
289 class SetupHiddenVolume : Command<CommandID::SEND_HIDDEN_VOLUME_SETUP> {
290 public:
291 constexpr static int MAX_HIDDEN_VOLUME_PASSWORD_SIZE = 20;
292 struct CommandPayload {
293 uint8_t SlotNr_u8;
294 uint8_t StartBlockPercent_u8;
295 uint8_t EndBlockPercent_u8;
296 uint8_t HiddenVolumePassword_au8[MAX_HIDDEN_VOLUME_PASSWORD_SIZE];
297 std::string dissect() const {
298 std::stringstream ss;
299 print_to_ss((int) SlotNr_u8);
300 print_to_ss((int) StartBlockPercent_u8);
301 print_to_ss((int) EndBlockPercent_u8);
302 print_to_ss_volatile(HiddenVolumePassword_au8);
303 return ss.str();
304 }
305 } __packed;
306
307 typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
308 CommandTransaction;
309 };
310
311
312 //disable this command for now
313 // class LockFirmware : public PasswordCommand<CommandID::SEND_LOCK_STICK_HARDWARE> {};
314
315 class ProductionTest : Command<CommandID::PRODUCTION_TEST> {
316 public:
317 struct ResponsePayload {
318
319 StorageCommandResponsePayload::TransmissionData transmission_data;
320
321 uint8_t FirmwareVersion_au8[2]; // 2 byte // 2
322 uint8_t FirmwareVersionInternal_u8; // 1 byte // 3
323 uint8_t SD_Card_Size_u8; // 1 byte // 4
324 uint32_t CPU_CardID_u32; // 4 byte // 8
325 uint32_t SmartCardID_u32; // 4 byte // 12
326 uint32_t SD_CardID_u32; // 4 byte // 16
327 uint8_t SC_UserPwRetryCount; // User PIN retry count 1 byte // 17
328 uint8_t SC_AdminPwRetryCount; // Admin PIN retry count 1 byte // 18
329 uint8_t SD_Card_ManufacturingYear_u8; // 1 byte // 19
330 uint8_t SD_Card_ManufacturingMonth_u8; // 1 byte // 20
331 uint16_t SD_Card_OEM_u16; // 2 byte // 22
332 uint16_t SD_WriteSpeed_u16; // in kbyte / sec 2 byte // 24
333 uint8_t SD_Card_Manufacturer_u8; // 1 byte // 25
334
335 bool isValid() const { return true; }
336
337 std::string dissect() const {
338 std::stringstream ss;
339
340 print_to_ss(transmission_data.dissect());
341 print_to_ss((int) FirmwareVersion_au8[0]);
342 print_to_ss((int) FirmwareVersion_au8[1]);
343 print_to_ss((int) FirmwareVersionInternal_u8);
344 print_to_ss((int) SD_Card_Size_u8);
345 print_to_ss( CPU_CardID_u32);
346 print_to_ss( SmartCardID_u32);
347 print_to_ss( SD_CardID_u32);
348 print_to_ss((int) SC_UserPwRetryCount);
349 print_to_ss((int) SC_AdminPwRetryCount);
350 print_to_ss((int) SD_Card_ManufacturingYear_u8);
351 print_to_ss((int) SD_Card_ManufacturingMonth_u8);
352 print_to_ss( SD_Card_OEM_u16);
353 print_to_ss( SD_WriteSpeed_u16);
354 print_to_ss((int) SD_Card_Manufacturer_u8);
355 return ss.str();
356 }
357
358 } __packed;
359
360 typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
361 CommandTransaction;
362 };
363
364 }
365 }
366 }
367
368 #undef print_to_ss
369 #pragma pack (pop)
370
371 #endif
0 libdir=@CMAKE_INSTALL_FULL_LIBDIR@
1 includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
2
3 Name: libnitrokey
4 Description: Library for communicating with Nitrokey in a clean and easy manner
5 Version: @libnitrokey_VERSION@
6 Requires.private: hidapi-libusb
7
8 Libs: -L${libdir} -lnitrokey
9 Cflags: -I${includedir}
0 # Created by and for Qt Creator. This file was created for editing the project sources only.
1 # You may attempt to use it for building too, by modifying this file here.
2
3 CONFIG += c++14 shared debug
4
5 TEMPLATE = lib
6 TARGET = nitrokey
7
8 VERSION = 3.2
9 QMAKE_TARGET_COMPANY = Nitrokey
10 QMAKE_TARGET_PRODUCT = libnitrokey
11 QMAKE_TARGET_DESCRIPTION = Communicate with Nitrokey stick devices in a clean and easy manner
12 QMAKE_TARGET_COPYRIGHT = Copyright (c) 2015-2018 Nitrokey UG
13
14 HEADERS = \
15 $$PWD/hidapi/hidapi/hidapi.h \
16 $$PWD/include/command.h \
17 $$PWD/include/command_id.h \
18 $$PWD/include/CommandFailedException.h \
19 $$PWD/include/cxx_semantics.h \
20 $$PWD/include/device.h \
21 $$PWD/include/device_proto.h \
22 $$PWD/include/DeviceCommunicationExceptions.h \
23 $$PWD/include/dissect.h \
24 $$PWD/include/LibraryException.h \
25 $$PWD/include/log.h \
26 $$PWD/include/LongOperationInProgressException.h \
27 $$PWD/include/misc.h \
28 $$PWD/include/NitrokeyManager.h \
29 $$PWD/include/stick10_commands.h \
30 $$PWD/include/stick10_commands_0.8.h \
31 $$PWD/include/stick20_commands.h \
32 $$PWD/NK_C_API.h
33
34 SOURCES = \
35 $$PWD/command_id.cc \
36 $$PWD/device.cc \
37 $$PWD/DeviceCommunicationExceptions.cpp \
38 $$PWD/log.cc \
39 $$PWD/misc.cc \
40 $$PWD/NitrokeyManager.cc \
41 $$PWD/NK_C_API.cc
42
43
44 tests {
45 SOURCES += \
46 $$PWD/unittest/catch_main.cpp \
47 $$PWD/unittest/test.cc \
48 $$PWD/unittest/test2.cc \
49 $$PWD/unittest/test3.cc \
50 $$PWD/unittest/test_C_API.cpp \
51 $$PWD/unittest/test_HOTP.cc
52 }
53
54 unix:!macx{
55 # SOURCES += $$PWD/hidapi/linux/hid.c
56 LIBS += -lhidapi-libusb
57 }
58
59 macx{
60 SOURCES += $$PWD/hidapi/mac/hid.c
61 LIBS+= -framework IOKit -framework CoreFoundation
62 }
63
64 win32 {
65 SOURCES += $$PWD/hidapi/windows/hid.c
66 LIBS += -lsetupapi
67 }
68
69 INCLUDEPATH = \
70 $$PWD/. \
71 $$PWD/hidapi/hidapi \
72 $$PWD/include \
73 $$PWD/include/hidapi \
74 $$PWD/unittest \
75 $$PWD/unittest/Catch/single_include
76
77 #DEFINES =
78
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include <iostream>
22 #include <string>
23 #include <ctime>
24 #include <iomanip>
25 #include "log.h"
26
27 #include <sstream>
28
29 namespace nitrokey {
30 namespace log {
31
32 Log *Log::mp_instance = nullptr;
33 StdlogHandler stdlog_handler;
34
35 std::string LogHandler::loglevel_to_str(Loglevel lvl) {
36 switch (lvl) {
37 case Loglevel::DEBUG_L1:
38 return std::string("DEBUG_L1");
39 case Loglevel::DEBUG_L2:
40 return std::string("DEBUG_L2");
41 case Loglevel::DEBUG:
42 return std::string("DEBUG");
43 case Loglevel::INFO:
44 return std::string("INFO");
45 case Loglevel::WARNING:
46 return std::string("WARNING");
47 case Loglevel::ERROR:
48 return std::string("ERROR");
49 }
50 return std::string("");
51 }
52
53 void Log::operator()(const std::string &logstr, Loglevel lvl) {
54 if (mp_loghandler != nullptr)
55 if ((int) lvl <= (int) m_loglevel) mp_loghandler->print(logstr, lvl);
56 }
57
58 void StdlogHandler::print(const std::string &str, Loglevel lvl) {
59 std::string s = format_message_to_string(str, lvl);
60 std::clog << s;
61 }
62
63 void FunctionalLogHandler::print(const std::string &str, Loglevel lvl) {
64 std::string s = format_message_to_string(str, lvl);
65 log_function(s);
66 }
67
68 std::string LogHandler::format_message_to_string(const std::string &str, const Loglevel &lvl) {
69 static bool last_short = false;
70 if (str.length() == 1){
71 last_short = true;
72 return str;
73 }
74 time_t t = time(nullptr);
75 tm tm = *localtime(&t);
76
77 std::stringstream s;
78 s
79 << (last_short? "\n" : "")
80 << "[" << std::put_time(&tm, "%c") << "]"
81 << "[" << loglevel_to_str(lvl) << "]\t"
82 << str << std::endl;
83 last_short = false;
84 return s.str();
85 }
86
87 FunctionalLogHandler::FunctionalLogHandler(log_function_type _log_function) {
88 log_function = _log_function;
89 }
90 }
91 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include <sstream>
22 #include <string>
23 #include "misc.h"
24 #include "inttypes.h"
25 #include <cstdlib>
26 #include <cstring>
27 #include "LibraryException.h"
28 #include <vector>
29
30 namespace nitrokey {
31 namespace misc {
32
33
34
35 ::std::vector<uint8_t> hex_string_to_byte(const char* hexString){
36 const size_t big_string_size = 256; //arbitrary 'big' number
37 const size_t s_size = strlen(hexString);
38 const size_t d_size = s_size/2;
39 if (s_size%2!=0 || s_size>big_string_size){
40 throw InvalidHexString(0);
41 }
42 auto data = ::std::vector<uint8_t>();
43 data.reserve(d_size);
44
45 char buf[2];
46 for(size_t i=0; i<s_size; i++){
47
48 char c = hexString[i];
49 bool char_from_range = (('0' <= c && c <='9') || ('A' <= c && c <= 'F') || ('a' <= c && c<= 'f'));
50 if (!char_from_range){
51 throw InvalidHexString(c);
52 }
53 buf[i%2] = c;
54 if (i%2==1){
55 data.push_back( strtoul(buf, NULL, 16) & 0xFF );
56 }
57 }
58 return data;
59 };
60
61 #include <cctype>
62 ::std::string hexdump(const uint8_t *p, size_t size, bool print_header,
63 bool print_ascii, bool print_empty) {
64 ::std::stringstream out;
65 char formatbuf[128];
66 const uint8_t *pstart = p;
67
68 for (const uint8_t *pend = p + size; p < pend;) {
69 if (print_header){
70 snprintf(formatbuf, 128, "%04x\t", static_cast<int> (p - pstart));
71 out << formatbuf;
72 }
73
74 const uint8_t* pp = p;
75 for (const uint8_t *le = p + 16; p < le; p++) {
76 if (p < pend){
77 snprintf(formatbuf, 128, "%02x ", uint8_t(*p));
78 out << formatbuf;
79 } else {
80 if(print_empty)
81 out << "-- ";
82 }
83
84 }
85 if(print_ascii){
86 out << " ";
87 for (const uint8_t *le = pp + 16; pp < le && pp < pend; pp++) {
88 if (std::isgraph(*pp))
89 out << uint8_t(*pp);
90 else
91 out << '.';
92 }
93 }
94 out << ::std::endl;
95 }
96 return out.str();
97 }
98
99 static uint32_t _crc32(uint32_t crc, uint32_t data) {
100 int i;
101 crc = crc ^ data;
102
103 for (i = 0; i < 32; i++) {
104 if (crc & 0x80000000)
105 crc = (crc << 1) ^ 0x04C11DB7; // polynomial used in STM32
106 else
107 crc = (crc << 1);
108 }
109
110 return crc;
111 }
112
113 uint32_t stm_crc32(const uint8_t *data, size_t size) {
114 uint32_t crc = 0xffffffff;
115 const uint32_t *pend = (const uint32_t *)(data + size);
116 for (const uint32_t *p = (const uint32_t *)(data); p < pend; p++)
117 crc = _crc32(crc, *p);
118 return crc;
119 }
120 }
121 }
0 #!/usr/bin/env python2
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 <http://www.gnu.org/licenses/>.
18
19 SPDX-License-Identifier: LGPL-3.0
20 """
21
22 import cffi
23 from enum import Enum
24
25 """
26 This example will print 10 HOTP codes from just written HOTP#2 slot.
27 For more examples of use please refer to unittest/test_*.py files.
28 """
29
30 ffi = cffi.FFI()
31 get_string = ffi.string
32
33 class DeviceErrorCode(Enum):
34 STATUS_OK = 0
35 NOT_PROGRAMMED = 3
36 WRONG_PASSWORD = 4
37 STATUS_NOT_AUTHORIZED = 5
38 STATUS_AES_DEC_FAILED = 0xa
39
40
41 def get_library():
42 fp = 'NK_C_API.h' # path to C API header
43
44 declarations = []
45 with open(fp, 'r') as f:
46 declarations = f.readlines()
47
48 cnt = 0
49 a = iter(declarations)
50 for declaration in a:
51 if declaration.strip().startswith('NK_C_API'):
52 declaration = declaration.replace('NK_C_API', '').strip()
53 while ';' not in declaration:
54 declaration += (next(a)).strip()
55 # print(declaration)
56 ffi.cdef(declaration, override=True)
57 cnt +=1
58 print('Imported {} declarations'.format(cnt))
59
60
61 C = None
62 import os, sys
63 path_build = os.path.join(".", "build")
64 paths = [
65 os.environ.get('LIBNK_PATH', None),
66 os.path.join(path_build,"libnitrokey.so"),
67 os.path.join(path_build,"libnitrokey.dylib"),
68 os.path.join(path_build,"libnitrokey.dll"),
69 os.path.join(path_build,"nitrokey.dll"),
70 ]
71 for p in paths:
72 if not p: continue
73 print("Trying " +p)
74 p = os.path.abspath(p)
75 if os.path.exists(p):
76 print("Found: "+p)
77 C = ffi.dlopen(p)
78 break
79 else:
80 print("File does not exist: " + p)
81 if not C:
82 print("No library file found")
83 print("Please set the path using LIBNK_PATH environment variable to existing library or compile it (see "
84 "README.md for details)")
85 sys.exit(1)
86
87 return C
88
89
90 def get_hotp_code(lib, i):
91 return get_string(lib.NK_get_hotp_code(i))
92
93 def to_hex(ss):
94 return ''.join([ format(ord(s),'02x') for s in ss ])
95
96 print('Warning!')
97 print('This example will change your configuration on inserted stick and overwrite your HOTP#2 slot.')
98 print('Please write "continue" to continue or any other string to quit')
99 a = raw_input()
100
101 if not a == 'continue':
102 exit()
103
104 ADMIN = raw_input('Please enter your admin PIN (empty string uses 12345678) ')
105 ADMIN = ADMIN or '12345678' # use default if empty string
106
107 show_log = raw_input('Should log messages be shown (please write "yes" to enable; this will make harder reading script output) ') == 'yes'
108 libnitrokey = get_library()
109
110 if show_log:
111 log_level = raw_input('Please select verbosity level (0-5, 2 is library default, 3 will be selected on empty input) ')
112 log_level = log_level or '3'
113 log_level = int(log_level)
114 libnitrokey.NK_set_debug_level(log_level)
115 else:
116 libnitrokey.NK_set_debug_level(2)
117
118
119 ADMIN_TEMP = '123123123'
120 RFC_SECRET = to_hex('12345678901234567890')
121
122 # libnitrokey.NK_login('S') # connect only to Nitrokey Storage device
123 # libnitrokey.NK_login('P') # connect only to Nitrokey Pro device
124 device_connected = libnitrokey.NK_login_auto() # connect to any Nitrokey Stick
125 if device_connected:
126 print('Connected to Nitrokey device!')
127 else:
128 print('Could not connect to Nitrokey device!')
129 exit()
130 use_8_digits = True
131 pin_correct = libnitrokey.NK_first_authenticate(ADMIN, ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
132 if pin_correct:
133 print('Your PIN is correct!')
134 else:
135 print('Your PIN is not correct! Please try again. Please be careful to not lock your stick!')
136 retry_count_left = libnitrokey.NK_get_admin_retry_count()
137 print('Retry count left: %d' % retry_count_left )
138 exit()
139
140 # For function parameters documentation please check NK_C_API.h
141 assert libnitrokey.NK_write_config(255, 255, 255, False, True, ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
142 libnitrokey.NK_first_authenticate(ADMIN, ADMIN_TEMP)
143 libnitrokey.NK_write_hotp_slot(1, 'python_test', RFC_SECRET, 0, use_8_digits, False, False, "",
144 ADMIN_TEMP)
145 # RFC test according to: https://tools.ietf.org/html/rfc4226#page-32
146 test_data = [
147 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871,
148 645520489,
149 ]
150 print('Getting HOTP code from Nitrokey Stick (RFC test, 8 digits): ')
151 for i in range(10):
152 hotp_slot_1_code = get_hotp_code(libnitrokey, 1)
153 correct_str = "correct!" if hotp_slot_1_code == str(test_data[i])[-8:] else "not correct"
154 print('%d: %s, should be %s -> %s' % (i, hotp_slot_1_code, str(test_data[i])[-8:], correct_str))
155 libnitrokey.NK_logout() # disconnect device
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
22 #include "catch.hpp"
0 """
1 Copyright (c) 2015-2018 Nitrokey UG
2
3 This file is part of libnitrokey.
4
5 libnitrokey is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 any later version.
9
10 libnitrokey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: LGPL-3.0
19 """
20
21 import pytest
22
23 from misc import ffi
24
25 device_type = None
26
27 def skip_if_device_version_lower_than(allowed_devices):
28 global device_type
29 model, version = device_type
30 infinite_version_number = 999
31 if allowed_devices.get(model, infinite_version_number) > version:
32 pytest.skip('This device model is not applicable to run this test')
33
34
35 @pytest.fixture(scope="module")
36 def C(request):
37 fp = '../NK_C_API.h'
38
39 declarations = []
40 with open(fp, 'r') as f:
41 declarations = f.readlines()
42
43 cnt = 0
44 a = iter(declarations)
45 for declaration in a:
46 if declaration.strip().startswith('NK_C_API'):
47 declaration = declaration.replace('NK_C_API', '').strip()
48 while ';' not in declaration:
49 declaration += (next(a)).strip()
50 # print(declaration)
51 ffi.cdef(declaration, override=True)
52 cnt +=1
53 print('Imported {} declarations'.format(cnt))
54
55 C = None
56 import os, sys
57 path_build = os.path.join("..", "build")
58 paths = [
59 os.environ.get('LIBNK_PATH', None),
60 os.path.join(path_build,"libnitrokey.so"),
61 os.path.join(path_build,"libnitrokey.dylib"),
62 os.path.join(path_build,"libnitrokey.dll"),
63 os.path.join(path_build,"nitrokey.dll"),
64 ]
65 for p in paths:
66 if not p: continue
67 print("Trying " +p)
68 p = os.path.abspath(p)
69 if os.path.exists(p):
70 print("Found: "+p)
71 C = ffi.dlopen(p)
72 break
73 else:
74 print("File does not exist: " + p)
75 if not C:
76 print("No library file found")
77 sys.exit(1)
78
79 C.NK_set_debug(False)
80 nk_login = C.NK_login_auto()
81 if nk_login != 1:
82 print('No devices detected!')
83 assert nk_login != 0 # returns 0 if not connected or wrong model or 1 when connected
84 global device_type
85 firmware_version = C.NK_get_major_firmware_version()
86 model = 'P' if firmware_version in [7,8] else 'S'
87 device_type = (model, firmware_version)
88
89 # assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
90 # assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK
91
92 # C.NK_status()
93
94 def fin():
95 print('\nFinishing connection to device')
96 C.NK_logout()
97 print('Finished')
98
99 request.addfinalizer(fin)
100 # C.NK_set_debug(True)
101 C.NK_set_debug_level(3)
102
103 return C
0 """
1 Copyright (c) 2015-2018 Nitrokey UG
2
3 This file is part of libnitrokey.
4
5 libnitrokey is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 any later version.
9
10 libnitrokey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: LGPL-3.0
19 """
20
21 from misc import to_hex
22
23 def bb(x):
24 return bytes(x, encoding='ascii')
25
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
42
43 class DeviceErrorCode:
44 STATUS_OK = 0
45 BUSY = 1 # busy or busy progressbar in place of wrong_CRC status
46 NOT_PROGRAMMED = 3
47 WRONG_PASSWORD = 4
48 STATUS_NOT_AUTHORIZED = 5
49 STATUS_AES_DEC_FAILED = 0xa
50
51
52 class LibraryErrors:
53 TOO_LONG_STRING = 200
54 INVALID_SLOT = 201
55 INVALID_HEX_STRING = 202
56 TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203
57
0 """
1 Copyright (c) 2015-2018 Nitrokey UG
2
3 This file is part of libnitrokey.
4
5 libnitrokey is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 any later version.
9
10 libnitrokey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: LGPL-3.0
19 """
20
21 import cffi
22
23 ffi = cffi.FFI()
24 gs = ffi.string
25
26
27 def to_hex(s):
28 return "".join("{:02x}".format(ord(c)) for c in s)
29
30
31 def wait(t):
32 import time
33 msg = 'Waiting for %d seconds' % t
34 print(msg.center(40, '='))
35 time.sleep(t)
36
37
38 def cast_pointer_to_tuple(obj, typen, len):
39 # usage:
40 # config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5)
41 return tuple(ffi.cast("%s [%d]" % (typen, len), obj)[0:len])
42
43
44 def get_devices_firmware_version(C):
45 firmware = C.NK_get_major_firmware_version()
46 return firmware
47
48
49 def is_pro_rtm_07(C):
50 firmware = get_devices_firmware_version(C)
51 return firmware == 7
52
53
54 def is_pro_rtm_08(C):
55 firmware = get_devices_firmware_version(C)
56 return firmware == 8
57
58
59 def is_storage(C):
60 """
61 exact firmware storage is sent by other function
62 """
63 # TODO identify connected device directly
64 return not is_pro_rtm_08(C) and not is_pro_rtm_07(C)
65
66
67 def is_long_OTP_secret_handled(C):
68 return is_pro_rtm_08(C) or is_storage(C) and get_devices_firmware_version(C) > 43
0 cffi
1 pytest-repeat
2 pytest-randomly
3 oath
0 #!/bin/bash
1
2 pip install -r requirements.txt --user
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include "catch.hpp"
22
23 #include <iostream>
24 #include <string.h>
25 #include "device_proto.h"
26 #include "log.h"
27 #include "stick10_commands.h"
28
29 using namespace std;
30 using namespace nitrokey::device;
31 using namespace nitrokey::proto::stick10;
32 using namespace nitrokey::log;
33 using namespace nitrokey::misc;
34
35 using Dev10 = std::shared_ptr<Stick10>;
36
37 std::string getSlotName(Dev10 stick, int slotNo) {
38 auto slot_req = get_payload<ReadSlot>();
39 slot_req.slot_number = slotNo;
40 auto slot = ReadSlot::CommandTransaction::run(stick, slot_req);
41 std::string sName(reinterpret_cast<char *>(slot.data().slot_name));
42 return sName;
43 }
44
45 TEST_CASE("Slot names are correct", "[slotNames]") {
46 auto stick = make_shared<Stick10>();
47 bool connected = stick->connect();
48 REQUIRE(connected == true);
49
50 Log::instance().set_loglevel(Loglevel::DEBUG);
51
52 auto resp = GetStatus::CommandTransaction::run(stick);
53
54 auto authreq = get_payload<FirstAuthenticate>();
55 strcpy((char *)(authreq.card_password), "12345678");
56 FirstAuthenticate::CommandTransaction::run(stick, authreq);
57
58 {
59 auto authreq = get_payload<EnablePasswordSafe>();
60 strcpy((char *)(authreq.user_password), "123456");
61 EnablePasswordSafe::CommandTransaction::run(stick, authreq);
62 }
63
64 //assuming these values were set earlier, thus failing on normal use
65 REQUIRE(getSlotName(stick, 0x20) == std::string("1"));
66 REQUIRE(getSlotName(stick, 0x21) == std::string("slot2"));
67
68 {
69 auto resp = GetPasswordRetryCount::CommandTransaction::run(stick);
70 REQUIRE(resp.data().password_retry_count == 3);
71 }
72 {
73 auto resp = GetUserPasswordRetryCount::CommandTransaction::run(stick);
74 REQUIRE(resp.data().password_retry_count == 3);
75 }
76
77 {
78 auto slot = get_payload<GetPasswordSafeSlotName>();
79 slot.slot_number = 0;
80 auto resp2 = GetPasswordSafeSlotName::CommandTransaction::run(stick, slot);
81 std::string sName(reinterpret_cast<char *>(resp2.data().slot_name));
82 REQUIRE(sName == std::string("web1"));
83 }
84
85 {
86 auto slot = get_payload<GetPasswordSafeSlotPassword>();
87 slot.slot_number = 0;
88 auto resp2 =
89 GetPasswordSafeSlotPassword::CommandTransaction::run(stick, slot);
90 std::string sName(reinterpret_cast<char *>(resp2.data().slot_password));
91 REQUIRE(sName == std::string("pass1"));
92 }
93
94 {
95 auto slot = get_payload<GetPasswordSafeSlotLogin>();
96 slot.slot_number = 0;
97 auto resp2 = GetPasswordSafeSlotLogin::CommandTransaction::run(stick, slot);
98 std::string sName(reinterpret_cast<char *>(resp2.data().slot_login));
99 REQUIRE(sName == std::string("login1"));
100 }
101
102 stick->disconnect();
103 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21
22 static const char *const default_admin_pin = "12345678";
23 static const char *const default_user_pin = "123456";
24
25 #include "catch.hpp"
26
27 #include <iostream>
28 #include <string.h>
29 #include <NitrokeyManager.h>
30 #include "device_proto.h"
31 #include "log.h"
32 //#include "stick10_commands.h"
33 #include "stick20_commands.h"
34
35 using namespace std;
36 using namespace nitrokey::device;
37 using namespace nitrokey::proto;
38 using namespace nitrokey::proto::stick20;
39 using namespace nitrokey::log;
40 using namespace nitrokey::misc;
41
42 #include <memory>
43
44 template<typename CMDTYPE>
45 void execute_password_command(std::shared_ptr<Device> stick, const char *password, const char kind = 'P') {
46 auto p = get_payload<CMDTYPE>();
47 if (kind == 'P'){
48 p.set_kind_user();
49 } else {
50 p.set_kind_admin();
51 }
52 strcpyT(p.password, password);
53 CMDTYPE::CommandTransaction::run(stick, p);
54 this_thread::sleep_for(1000ms);
55 }
56
57 /**
58 * fail on purpose (will result in failed test)
59 * disable from running unwillingly
60 */
61 void SKIP_TEST() {
62 CAPTURE("Failing current test to SKIP it");
63 REQUIRE(false);
64 }
65
66
67 TEST_CASE("long operation test", "[test_long]") {
68 SKIP_TEST();
69
70 auto stick = make_shared<Stick20>();
71 bool connected = stick->connect();
72 REQUIRE(connected == true);
73 Log::instance().set_loglevel(Loglevel::DEBUG);
74 try{
75 auto p = get_payload<FillSDCardWithRandomChars>();
76 p.set_defaults();
77 strcpyT(p.admin_pin, default_admin_pin);
78 FillSDCardWithRandomChars::CommandTransaction::run(stick, p);
79 this_thread::sleep_for(1000ms);
80
81 CHECK(false);
82 }
83 catch (LongOperationInProgressException &progressException){
84 CHECK(true);
85 }
86
87
88 for (int i = 0; i < 30; ++i) {
89 try {
90 stick10::GetStatus::CommandTransaction::run(stick);
91 }
92 catch (LongOperationInProgressException &progressException){
93 CHECK((int)progressException.progress_bar_value>=0);
94 CAPTURE((int)progressException.progress_bar_value);
95 this_thread::sleep_for(2000ms);
96 }
97
98 }
99
100 }
101
102
103 TEST_CASE("test device internal status with various commands", "[fast]") {
104 auto stick = make_shared<Stick20>();
105 bool connected = stick->connect();
106 REQUIRE(connected == true);
107
108 Log::instance().set_loglevel(Loglevel::DEBUG);
109 auto p = get_payload<stick20::SendStartup>();
110 p.set_defaults();
111 auto device_status = stick20::SendStartup::CommandTransaction::run(stick, p);
112 REQUIRE(device_status.data().AdminPwRetryCount == 3);
113 REQUIRE(device_status.data().UserPwRetryCount == 3);
114 REQUIRE(device_status.data().ActiveSmartCardID_u32 != 0);
115
116 auto production_status = stick20::ProductionTest::CommandTransaction::run(stick);
117 REQUIRE(production_status.data().SD_Card_Size_u8 == 8);
118 REQUIRE(production_status.data().SD_CardID_u32 != 0);
119
120 auto sdcard_occupancy = stick20::GetSDCardOccupancy::CommandTransaction::run(stick);
121 REQUIRE((int) sdcard_occupancy.data().ReadLevelMin >= 0);
122 REQUIRE((int) sdcard_occupancy.data().ReadLevelMax <= 100);
123 REQUIRE((int) sdcard_occupancy.data().WriteLevelMin >= 0);
124 REQUIRE((int) sdcard_occupancy.data().WriteLevelMax <= 100);
125 }
126
127 TEST_CASE("setup hidden volume test", "[hidden]") {
128 auto stick = make_shared<Stick20>();
129 bool connected = stick->connect();
130 REQUIRE(connected == true);
131 Log::instance().set_loglevel(Loglevel::DEBUG);
132 stick10::LockDevice::CommandTransaction::run(stick);
133 this_thread::sleep_for(2000ms);
134
135 auto user_pin = default_user_pin;
136 execute_password_command<EnableEncryptedPartition>(stick, user_pin);
137
138 auto p = get_payload<stick20::SetupHiddenVolume>();
139 p.SlotNr_u8 = 0;
140 p.StartBlockPercent_u8 = 70;
141 p.EndBlockPercent_u8 = 90;
142 auto hidden_volume_password = "123123123";
143 strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password);
144 stick20::SetupHiddenVolume::CommandTransaction::run(stick, p);
145 this_thread::sleep_for(2000ms);
146
147 execute_password_command<EnableHiddenEncryptedPartition>(stick, hidden_volume_password);
148 }
149
150 TEST_CASE("setup multiple hidden volumes", "[hidden]") {
151 auto stick = make_shared<Stick20>();
152 bool connected = stick->connect();
153 REQUIRE(connected == true);
154 Log::instance().set_loglevel(Loglevel::DEBUG);
155
156 auto user_pin = default_user_pin;
157 stick10::LockDevice::CommandTransaction::run(stick);
158 this_thread::sleep_for(2000ms);
159 execute_password_command<EnableEncryptedPartition>(stick, user_pin);
160
161 constexpr int volume_count = 4;
162 for (int i = 0; i < volume_count; ++i) {
163 auto p = get_payload<stick20::SetupHiddenVolume>();
164 p.SlotNr_u8 = i;
165 p.StartBlockPercent_u8 = 20 + 10*i;
166 p.EndBlockPercent_u8 = p.StartBlockPercent_u8+i+1;
167 auto hidden_volume_password = std::string("123123123")+std::to_string(i);
168 strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password.c_str());
169 stick20::SetupHiddenVolume::CommandTransaction::run(stick, p);
170 this_thread::sleep_for(2000ms);
171 }
172
173
174 for (int i = 0; i < volume_count; ++i) {
175 execute_password_command<EnableEncryptedPartition>(stick, user_pin);
176 auto hidden_volume_password = std::string("123123123")+std::to_string(i);
177 execute_password_command<EnableHiddenEncryptedPartition>(stick, hidden_volume_password.c_str());
178 this_thread::sleep_for(2000ms);
179 }
180 }
181
182
183 //in case of a bug this could change update PIN to some unexpected value
184 // - please save log with packet dump if this test will not pass
185 TEST_CASE("update password change", "[dangerous]") {
186 SKIP_TEST();
187
188 auto stick = make_shared<Stick20>();
189 bool connected = stick->connect();
190 REQUIRE(connected == true);
191 Log::instance().set_loglevel(Loglevel::DEBUG);
192
193 auto pass1 = default_admin_pin;
194 auto pass2 = "12345678901234567890";
195
196 auto data = {
197 make_pair(pass1, pass2),
198 make_pair(pass2, pass1),
199 };
200 for (auto && password: data) {
201 auto p = get_payload<stick20::ChangeUpdatePassword>();
202 strcpyT(p.current_update_password, password.first);
203 strcpyT(p.new_update_password, password.second);
204 stick20::ChangeUpdatePassword::CommandTransaction::run(stick, p);
205 }
206 }
207
208 TEST_CASE("general test", "[test]") {
209 auto stick = make_shared<Stick20>();
210 bool connected = stick->connect();
211 REQUIRE(connected == true);
212
213 Log::instance().set_loglevel(Loglevel::DEBUG);
214
215 stick10::LockDevice::CommandTransaction::run(stick);
216 // execute_password_command<EnableEncryptedPartition>(stick, "123456");
217 // execute_password_command<DisableEncryptedPartition>(stick, "123456");
218 // execute_password_command<DisableHiddenEncryptedPartition>(stick, "123123123");
219
220 execute_password_command<SendSetReadonlyToUncryptedVolume>(stick, default_user_pin);
221 execute_password_command<SendSetReadwriteToUncryptedVolume>(stick, default_user_pin);
222 execute_password_command<SendClearNewSdCardFound>(stick, default_admin_pin, 'A');
223 stick20::GetDeviceStatus::CommandTransaction::run(stick);
224 this_thread::sleep_for(1000ms);
225 // execute_password_command<LockFirmware>(stick, "123123123"); //CAUTION
226 // execute_password_command<EnableFirmwareUpdate>(stick, "123123123"); //CAUTION FIRMWARE PIN
227
228 execute_password_command<ExportFirmware>(stick, "12345678", 'A');
229 // execute_password_command<FillSDCardWithRandomChars>(stick, "12345678", 'A');
230
231 stick10::LockDevice::CommandTransaction::run(stick);
232 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
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 "catch.hpp"
28
29 #include <iostream>
30 #include <string.h>
31 #include <NitrokeyManager.h>
32 #include "device_proto.h"
33 #include "log.h"
34 #include "stick10_commands_0.8.h"
35 //#include "stick20_commands.h"
36
37 using namespace std;
38 using namespace nitrokey::device;
39 using namespace nitrokey::proto;
40 using namespace nitrokey::proto::stick10_08;
41 using namespace nitrokey::log;
42 using namespace nitrokey::misc;
43
44 using Dev = Stick10;
45 using Dev10 = std::shared_ptr<Dev>;
46
47 void connect_and_setup(Dev10 stick) {
48 bool connected = stick->connect();
49 REQUIRE(connected == true);
50 Log::instance().set_loglevel(Loglevel::DEBUG);
51 }
52
53 void authorize(Dev10 stick) {
54 auto authreq = get_payload<FirstAuthenticate>();
55 strcpy((char *) (authreq.card_password), default_admin_pin);
56 strcpy((char *) (authreq.temporary_password), temporary_password);
57 FirstAuthenticate::CommandTransaction::run(stick, authreq);
58
59 auto user_auth = get_payload<UserAuthenticate>();
60 strcpyT(user_auth.temporary_password, temporary_password);
61 strcpyT(user_auth.card_password, default_user_pin);
62 UserAuthenticate::CommandTransaction::run(stick, user_auth);
63 }
64
65 TEST_CASE("write slot", "[pronew]"){
66 auto stick = make_shared<Dev>();
67
68 connect_and_setup(stick);
69 authorize(stick);
70
71 auto p2 = get_payload<SendOTPData>();
72 strcpyT(p2.temporary_admin_password, temporary_password);
73 p2.setTypeName();
74 strcpyT(p2.data, "test name aaa");
75 stick10_08::SendOTPData::CommandTransaction::run(stick, p2);
76
77 p2 = get_payload<SendOTPData>();
78 strcpyT(p2.temporary_admin_password, temporary_password);
79 strcpyT(p2.data, RFC_SECRET);
80 p2.setTypeSecret();
81 stick10_08::SendOTPData::CommandTransaction::run(stick, p2);
82
83 auto p = get_payload<WriteToOTPSlot>();
84 strcpyT(p.temporary_admin_password, temporary_password);
85 p.use_8_digits = true;
86 p.slot_number = 0 + 0x10;
87 p.slot_counter_or_interval = 0;
88 stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p);
89
90 auto pc = get_payload<WriteGeneralConfig>();
91 pc.enable_user_password = 0;
92 strcpyT(pc.temporary_admin_password, temporary_password);
93 WriteGeneralConfig::CommandTransaction::run(stick, pc);
94
95 auto p3 = get_payload<GetHOTP>();
96 p3.slot_number = 0 + 0x10;
97 GetHOTP::CommandTransaction::run(stick, p3);
98
99 }
100
101
102 TEST_CASE("erase slot", "[pronew]"){
103 auto stick = make_shared<Dev>();
104 connect_and_setup(stick);
105 authorize(stick);
106
107 auto p = get_payload<WriteGeneralConfig>();
108 p.enable_user_password = 0;
109 strcpyT(p.temporary_admin_password, temporary_password);
110 WriteGeneralConfig::CommandTransaction::run(stick, p);
111
112 auto p3 = get_payload<GetHOTP>();
113 p3.slot_number = 0 + 0x10;
114 GetHOTP::CommandTransaction::run(stick, p3);
115
116 auto erase_payload = get_payload<EraseSlot>();
117 erase_payload.slot_number = 0 + 0x10;
118 strcpyT(erase_payload.temporary_admin_password, temporary_password);
119 EraseSlot::CommandTransaction::run(stick, erase_payload);
120
121 auto p4 = get_payload<GetHOTP>();
122 p4.slot_number = 0 + 0x10;
123 REQUIRE_THROWS(
124 GetHOTP::CommandTransaction::run(stick, p4)
125 );
126 }
127
128 TEST_CASE("write general config", "[pronew]") {
129 auto stick = make_shared<Dev>();
130 connect_and_setup(stick);
131 authorize(stick);
132
133 auto p = get_payload<WriteGeneralConfig>();
134 p.enable_user_password = 1;
135 REQUIRE_THROWS(
136 WriteGeneralConfig::CommandTransaction::run(stick, p)
137 );
138 strcpyT(p.temporary_admin_password, temporary_password);
139 WriteGeneralConfig::CommandTransaction::run(stick, p);
140 }
141
142 TEST_CASE("authorize user HOTP", "[pronew]") {
143 auto stick = make_shared<Dev>();
144 connect_and_setup(stick);
145 authorize(stick);
146
147 {
148 auto p = get_payload<WriteGeneralConfig>();
149 p.enable_user_password = 1;
150 strcpyT(p.temporary_admin_password, temporary_password);
151 WriteGeneralConfig::CommandTransaction::run(stick, p);
152 }
153
154 auto p2 = get_payload<SendOTPData>();
155 strcpyT(p2.temporary_admin_password, temporary_password);
156 p2.setTypeName();
157 strcpyT(p2.data, "test name aaa");
158 stick10_08::SendOTPData::CommandTransaction::run(stick, p2);
159
160 p2 = get_payload<SendOTPData>();
161 strcpyT(p2.temporary_admin_password, temporary_password);
162 strcpyT(p2.data, RFC_SECRET);
163 p2.setTypeSecret();
164 stick10_08::SendOTPData::CommandTransaction::run(stick, p2);
165
166 auto p = get_payload<WriteToOTPSlot>();
167 strcpyT(p.temporary_admin_password, temporary_password);
168 p.use_8_digits = true;
169 p.slot_number = 0 + 0x10;
170 p.slot_counter_or_interval = 0;
171 stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p);
172
173
174 auto p3 = get_payload<GetHOTP>();
175 p3.slot_number = 0 + 0x10;
176 REQUIRE_THROWS(
177 GetHOTP::CommandTransaction::run(stick, p3)
178 );
179 strcpyT(p3.temporary_user_password, temporary_password);
180 auto code_response = GetHOTP::CommandTransaction::run(stick, p3);
181 REQUIRE(code_response.data().code == 84755224);
182
183 }
184
185 TEST_CASE("check firmware version", "[pronew]") {
186 auto stick = make_shared<Dev>();
187 connect_and_setup(stick);
188
189 auto p = GetStatus::CommandTransaction::run(stick);
190 REQUIRE(p.data().firmware_version == 8);
191 }
192
193 TEST_CASE("authorize user TOTP", "[pronew]") {
194 auto stick = make_shared<Dev>();
195 connect_and_setup(stick);
196 authorize(stick);
197
198 {
199 auto p = get_payload<WriteGeneralConfig>();
200 p.enable_user_password = 1;
201 strcpyT(p.temporary_admin_password, temporary_password);
202 WriteGeneralConfig::CommandTransaction::run(stick, p);
203 }
204
205 auto p2 = get_payload<SendOTPData>();
206 strcpyT(p2.temporary_admin_password, temporary_password);
207 p2.setTypeName();
208 strcpyT(p2.data, "test name TOTP");
209 stick10_08::SendOTPData::CommandTransaction::run(stick, p2);
210
211 p2 = get_payload<SendOTPData>();
212 strcpyT(p2.temporary_admin_password, temporary_password);
213 strcpyT(p2.data, RFC_SECRET);
214 p2.setTypeSecret();
215 stick10_08::SendOTPData::CommandTransaction::run(stick, p2);
216
217 auto p = get_payload<WriteToOTPSlot>();
218 strcpyT(p.temporary_admin_password, temporary_password);
219 p.use_8_digits = true;
220 p.slot_number = 0 + 0x20;
221 p.slot_counter_or_interval = 30;
222 stick10_08::WriteToOTPSlot::CommandTransaction::run(stick, p);
223
224 auto p_get_totp = get_payload<GetTOTP>();
225 p_get_totp.slot_number = 0 + 0x20;
226
227 REQUIRE_THROWS(
228 GetTOTP::CommandTransaction::run(stick, p_get_totp)
229 );
230 strcpyT(p_get_totp.temporary_user_password, temporary_password);
231
232 auto p_set_time = get_payload<SetTime>();
233 p_set_time.reset = 1;
234 p_set_time.time = 59;
235 SetTime::CommandTransaction::run(stick, p_set_time);
236 auto code = GetTOTP::CommandTransaction::run(stick, p_get_totp);
237 REQUIRE(code.data().code == 94287082);
238
239 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 static const int TOO_LONG_STRING = 200;
22
23 #include "catch.hpp"
24
25 #include <iostream>
26 #include <string>
27 #include "log.h"
28 #include "../NK_C_API.h"
29
30 TEST_CASE("C API connect", "[BASIC]") {
31 auto login = NK_login_auto();
32 REQUIRE(login != 0);
33 NK_logout();
34 login = NK_login_auto();
35 REQUIRE(login != 0);
36 NK_logout();
37 login = NK_login_auto();
38 REQUIRE(login != 0);
39 }
40
41 TEST_CASE("Check retry count", "[BASIC]") {
42 REQUIRE(NK_get_admin_retry_count() == 3);
43 REQUIRE(NK_get_user_retry_count() == 3);
44 }
45
46 TEST_CASE("Check long strings", "[STANDARD]") {
47 const char* longPin = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
48 const char* pin = "123123123";
49 auto result = NK_change_user_PIN(longPin, pin);
50 REQUIRE(result == TOO_LONG_STRING);
51 result = NK_change_user_PIN(pin, longPin);
52 REQUIRE(result == TOO_LONG_STRING);
53 CAPTURE(result);
54 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21
22 #include "catch.hpp"
23 #include <iostream>
24 #include "device_proto.h"
25 #include "log.h"
26 #include "stick10_commands.h"
27 #include <cstdlib>
28 #include "misc.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 void hexStringToByte(uint8_t data[], const char* hexString){
37 REQUIRE(strlen(hexString)%2==0);
38 char buf[2];
39 for(int i=0; i<strlen(hexString); i++){
40 buf[i%2] = hexString[i];
41 if (i%2==1){
42 data[i/2] = strtoul(buf, NULL, 16) & 0xFF;
43 }
44 }
45 };
46
47 TEST_CASE("test secret", "[functions]") {
48 uint8_t slot_secret[21];
49 slot_secret[20] = 0;
50 const char* secretHex = "3132333435363738393031323334353637383930";
51 hexStringToByte(slot_secret, secretHex);
52 CAPTURE(slot_secret);
53 REQUIRE(strcmp("12345678901234567890",reinterpret_cast<char *>(slot_secret) ) == 0 );
54 }
55
56 TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") {
57 std::shared_ptr<Stick10> stick = make_shared<Stick10>();
58 bool connected = stick->connect();
59
60 REQUIRE(connected == true);
61
62 Log::instance().set_loglevel(Loglevel::DEBUG);
63
64 auto resp = GetStatus::CommandTransaction::run(stick);
65
66 const char * temporary_password = "123456789012345678901234";
67 {
68 auto authreq = get_payload<FirstAuthenticate>();
69 strcpy((char *)(authreq.card_password), "12345678");
70 strcpy((char *)(authreq.temporary_password), temporary_password);
71 FirstAuthenticate::CommandTransaction::run(stick, authreq);
72 }
73
74 //test according to https://tools.ietf.org/html/rfc4226#page-32
75 {
76 auto hwrite = get_payload<WriteToHOTPSlot>();
77 hwrite.slot_number = 0x10;
78 strcpy(reinterpret_cast<char *>(hwrite.slot_name), "rfc4226_lib");
79 //strcpy(reinterpret_cast<char *>(hwrite.slot_secret), "");
80 const char* secretHex = "3132333435363738393031323334353637383930";
81 hexStringToByte(hwrite.slot_secret, secretHex);
82
83 //hwrite.slot_config; //TODO check various configs in separate test cases
84 //strcpy(reinterpret_cast<char *>(hwrite.slot_token_id), "");
85 //strcpy(reinterpret_cast<char *>(hwrite.slot_counter), "");
86
87 //authorize writehotp first
88 {
89 auto auth = get_payload<Authorize>();
90 strcpy((char *)(auth.temporary_password), temporary_password);
91 auth.crc_to_authorize = WriteToHOTPSlot::CommandTransaction::getCRC(hwrite);
92 Authorize::CommandTransaction::run(stick, auth);
93 }
94
95 //run hotp command
96 WriteToHOTPSlot::CommandTransaction::run(stick, hwrite);
97
98 uint32_t codes[] = {
99 755224, 287082, 359152, 969429, 338314,
100 254676, 287922, 162583, 399871, 520489
101 };
102
103 for( auto code: codes){
104 auto gh = get_payload<GetHOTP>();
105 gh.slot_number = 0x10;
106 auto resp = GetHOTP::CommandTransaction::run(stick, gh);
107 REQUIRE( resp.data().code == code);
108 }
109 //checking slot programmed before with nitro-app
110 /*
111 for( auto code: codes){
112 GetHOTP::CommandTransaction::CommandPayload gh;
113 gh.slot_number = 0x12;
114 auto resp = GetHOTP::CommandTransaction::run(stick, gh);
115 REQUIRE( resp.code == code);
116 }
117 */
118 }
119
120
121 stick->disconnect();
122 }
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #ifndef LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H
22 #define LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H
23
24 #define STICK20_CMD_START_VALUE 0x20
25 #define STICK20_CMD_ENABLE_CRYPTED_PARI (STICK20_CMD_START_VALUE + 0)
26 #define STICK20_CMD_DISABLE_CRYPTED_PARI (STICK20_CMD_START_VALUE + 1)
27 #define STICK20_CMD_ENABLE_HIDDEN_CRYPTED_PARI (STICK20_CMD_START_VALUE + 2)
28 #define STICK20_CMD_DISABLE_HIDDEN_CRYPTED_PARI (STICK20_CMD_START_VALUE + 3)
29 #define STICK20_CMD_ENABLE_FIRMWARE_UPDATE (STICK20_CMD_START_VALUE + 4)
30 #define STICK20_CMD_EXPORT_FIRMWARE_TO_FILE (STICK20_CMD_START_VALUE + 5)
31 #define STICK20_CMD_GENERATE_NEW_KEYS (STICK20_CMD_START_VALUE + 6)
32 #define STICK20_CMD_FILL_SD_CARD_WITH_RANDOM_CHARS (STICK20_CMD_START_VALUE + 7)
33
34 #define STICK20_CMD_WRITE_STATUS_DATA (STICK20_CMD_START_VALUE + 8)
35 #define STICK20_CMD_ENABLE_READONLY_UNCRYPTED_LUN (STICK20_CMD_START_VALUE + 9)
36 #define STICK20_CMD_ENABLE_READWRITE_UNCRYPTED_LUN (STICK20_CMD_START_VALUE + 10)
37
38 #define STICK20_CMD_SEND_PASSWORD_MATRIX (STICK20_CMD_START_VALUE + 11)
39 #define STICK20_CMD_SEND_PASSWORD_MATRIX_PINDATA (STICK20_CMD_START_VALUE + 12)
40 #define STICK20_CMD_SEND_PASSWORD_MATRIX_SETUP (STICK20_CMD_START_VALUE + 13)
41
42 #define STICK20_CMD_GET_DEVICE_STATUS (STICK20_CMD_START_VALUE + 14)
43 #define STICK20_CMD_SEND_DEVICE_STATUS (STICK20_CMD_START_VALUE + 15)
44
45 #define STICK20_CMD_SEND_HIDDEN_VOLUME_PASSWORD (STICK20_CMD_START_VALUE + 16)
46 #define STICK20_CMD_SEND_HIDDEN_VOLUME_SETUP (STICK20_CMD_START_VALUE + 17)
47 #define STICK20_CMD_SEND_PASSWORD (STICK20_CMD_START_VALUE + 18)
48 #define STICK20_CMD_SEND_NEW_PASSWORD (STICK20_CMD_START_VALUE + 19)
49 #define STICK20_CMD_CLEAR_NEW_SD_CARD_FOUND (STICK20_CMD_START_VALUE + 20)
50
51 #define STICK20_CMD_SEND_STARTUP (STICK20_CMD_START_VALUE + 21)
52 #define STICK20_CMD_SEND_CLEAR_STICK_KEYS_NOT_INITIATED (STICK20_CMD_START_VALUE + 22)
53 #define STICK20_CMD_SEND_LOCK_STICK_HARDWARE (STICK20_CMD_START_VALUE + 23)
54
55 #define STICK20_CMD_PRODUCTION_TEST (STICK20_CMD_START_VALUE + 24)
56 #define STICK20_CMD_SEND_DEBUG_DATA (STICK20_CMD_START_VALUE + 25)
57
58 #define STICK20_CMD_CHANGE_UPDATE_PIN (STICK20_CMD_START_VALUE + 26)
59
60
61 #endif //LIBNITROKEY_TEST_COMMAND_IDS_HEADER_H_H
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21
22 const char * const default_admin_pin = "12345678";
23 const char * const default_user_pin = "123456";
24 const char * const temporary_password = "123456789012345678901234";
25 const char * const RFC_SECRET = "12345678901234567890";
26 const char * const hidden_volume_pass = "123456789012345";
27
28 #include "catch.hpp"
29
30 #include <NitrokeyManager.h>
31
32 using namespace std;
33 using namespace nitrokey;
34
35
36 bool test_36(){
37 auto i = NitrokeyManager::instance();
38 i->set_loglevel(3);
39 REQUIRE(i->connect());
40
41 for (int j = 0; j < 200; ++j) {
42 i->get_status();
43 i->get_status_storage_as_string();
44 INFO( "Iteration: " << j);
45 }
46 return true;
47 }
48
49 bool test_31(){
50 auto i = NitrokeyManager::instance();
51 i->set_loglevel(3);
52 REQUIRE(i->connect());
53
54 // i->unlock_encrypted_volume(default_user_pin);
55 // i->create_hidden_volume(0, 70, 80, hidden_volume_pass);
56 // i->lock_device();
57
58 try{
59 i->get_password_safe_slot_status();
60 }
61 catch (...){
62 //pass
63 }
64
65 i->get_status_storage();
66 i->get_admin_retry_count();
67 i->get_status_storage();
68 i->get_user_retry_count();
69 i->unlock_encrypted_volume(default_user_pin);
70 i->get_status_storage();
71 i->get_password_safe_slot_status();
72 i->get_status_storage();
73 i->get_user_retry_count();
74 i->get_password_safe_slot_status();
75 i->get_status();
76 i->get_status_storage();
77 i->get_admin_retry_count();
78 i->get_status();
79 i->get_user_retry_count();
80 i->unlock_hidden_volume(hidden_volume_pass);
81 i->get_status_storage();
82 i->get_password_safe_slot_status();
83
84
85 return true;
86 }
87
88 TEST_CASE("issue 31", "[issue]"){
89 for(int i=0; i<20; i++){
90 REQUIRE(test_31());
91 }
92 }
93
94
95
96 TEST_CASE("issue 36", "[issue]"){
97 REQUIRE(test_36());
98 }
0 """
1 Copyright (c) 2015-2018 Nitrokey UG
2
3 This file is part of libnitrokey.
4
5 libnitrokey is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 any later version.
9
10 libnitrokey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: LGPL-3.0
19 """
20
21 import pytest
22
23 from misc import ffi, gs, to_hex, is_pro_rtm_07, is_long_OTP_secret_handled
24 from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, LibraryErrors, bbRFC_SECRET
25
26
27 def test_too_long_strings(C):
28 new_password = b'123123123'
29 long_string = b'a' * 100
30 assert C.NK_change_user_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING
31 assert C.NK_change_user_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING
32 assert C.NK_change_admin_PIN(long_string, new_password) == LibraryErrors.TOO_LONG_STRING
33 assert C.NK_change_admin_PIN(new_password, long_string) == LibraryErrors.TOO_LONG_STRING
34 assert C.NK_first_authenticate(long_string, DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING
35 assert C.NK_erase_totp_slot(0, long_string) == LibraryErrors.TOO_LONG_STRING
36 digits = False
37 assert C.NK_write_hotp_slot(1, long_string, bbRFC_SECRET, 0, digits, False, False, b"",
38 DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TOO_LONG_STRING
39 assert C.NK_write_hotp_slot(1, b'long_test', bbRFC_SECRET, 0, digits, False, False, b"",
40 long_string) == LibraryErrors.TOO_LONG_STRING
41 assert gs(C.NK_get_hotp_code_PIN(0, long_string)) == b""
42 assert C.NK_get_last_command_status() == LibraryErrors.TOO_LONG_STRING
43
44
45 def test_invalid_slot(C):
46 invalid_slot = 255
47 assert C.NK_erase_totp_slot(invalid_slot, b'some password') == LibraryErrors.INVALID_SLOT
48 assert C.NK_write_hotp_slot(invalid_slot, b'long_test', bbRFC_SECRET, 0, False, False, False, b"",
49 b'aaa') == LibraryErrors.INVALID_SLOT
50 assert gs(C.NK_get_hotp_code_PIN(invalid_slot, b'some password')) == b""
51 assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
52 assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT
53 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
54 assert gs(C.NK_get_password_safe_slot_name(invalid_slot)) == b''
55 assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
56 assert gs(C.NK_get_password_safe_slot_login(invalid_slot)) == b''
57 assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT
58
59
60 @pytest.mark.parametrize("invalid_hex_string",
61 ['text', '00 ', '0xff', 'zzzzzzzzzzzz', 'fff', 'f' * 257, 'f' * 258])
62 def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string):
63 """
64 Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number,
65 empty or longer than 255 characters.
66 """
67 invalid_hex_string = invalid_hex_string.encode('ascii')
68 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
69 assert C.NK_write_hotp_slot(1, b'slot_name', invalid_hex_string, 0, True, False, False, b'',
70 DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING
71 assert C.NK_write_totp_slot(1, b'python_test', invalid_hex_string, 30, True, False, False, b"",
72 DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING
73
74 def test_warning_binary_bigger_than_secret_buffer(C):
75 invalid_hex_string = to_hex('1234567890') * 3
76 invalid_hex_string = invalid_hex_string.encode('ascii')
77 if is_long_OTP_secret_handled(C):
78 invalid_hex_string *= 2
79 assert C.NK_write_hotp_slot(1, b'slot_name', invalid_hex_string, 0, True, False, False, b'',
80 DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE
81
82
83 @pytest.mark.skip(reason='Experimental')
84 def test_clear(C):
85 d = 'asdasdasd'
86 print(d)
87 C.clear_password(d)
88 print(d)
0 /*
1 * Copyright (c) 2015-2018 Nitrokey UG
2 *
3 * This file is part of libnitrokey.
4 *
5 * libnitrokey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * libnitrokey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: LGPL-3.0
19 */
20
21 #include "catch.hpp"
22 #include <NitrokeyManager.h>
23 #include <memory>
24 #include "../NK_C_API.h"
25
26 using namespace nitrokey::proto;
27 using namespace nitrokey::device;
28
29 using namespace std;
30 using namespace nitrokey;
31
32 //This test suite assumes no Pro or Storage devices are connected
33
34 TEST_CASE("Return false on no device connected", "[fast]") {
35 INFO("This test case assumes no Pro or Storage devices are connected");
36 auto stick = make_shared<Stick20>();
37 bool connected = true;
38 REQUIRE_NOTHROW(connected = stick->connect());
39 REQUIRE_FALSE(connected);
40
41 auto stick_pro = make_shared<Stick10>();
42 REQUIRE_NOTHROW(connected = stick_pro->connect());
43 REQUIRE_FALSE(connected);
44
45
46 auto i = NitrokeyManager::instance();
47 REQUIRE_NOTHROW(connected = i->connect());
48 REQUIRE_FALSE(connected);
49 REQUIRE_FALSE(i->is_connected());
50 REQUIRE_FALSE(i->disconnect());
51 REQUIRE_FALSE(i->could_current_device_be_enumerated());
52
53
54 int C_connected = 1;
55 REQUIRE_NOTHROW(C_connected = NK_login_auto());
56 REQUIRE(0 == C_connected);
57 }
58
59 TEST_CASE("Test C++ side behaviour in offline", "[fast]") {
60 auto i = NitrokeyManager::instance();
61
62 string serial_number;
63 REQUIRE_NOTHROW (serial_number = i->get_serial_number());
64 REQUIRE(serial_number.empty());
65
66 REQUIRE_THROWS_AS(
67 i->get_status(), DeviceNotConnected
68 );
69
70 REQUIRE_THROWS_AS(
71 i->get_HOTP_code(0xFF, ""), InvalidSlotException
72 );
73
74 REQUIRE_THROWS_AS(
75 i->get_TOTP_code(0xFF, ""), InvalidSlotException
76 );
77
78 REQUIRE_THROWS_AS(
79 i->erase_hotp_slot(0xFF, ""), InvalidSlotException
80 );
81
82 REQUIRE_THROWS_AS(
83 i->erase_totp_slot(0xFF, ""), InvalidSlotException
84 );
85
86 REQUIRE_THROWS_AS(
87 i->get_totp_slot_name(0xFF), InvalidSlotException
88 );
89
90 REQUIRE_THROWS_AS(
91 i->get_hotp_slot_name(0xFF), InvalidSlotException
92 );
93
94 REQUIRE_THROWS_AS(
95 i->first_authenticate("123123", "123123"), DeviceNotConnected
96 );
97
98 REQUIRE_THROWS_AS(
99 i->get_connected_device_model(), DeviceNotConnected
100 );
101
102 REQUIRE_THROWS_AS(
103 i->clear_new_sd_card_warning("123123"), DeviceNotConnected
104 );
105
106 }
107
108
109 TEST_CASE("Test helper function - hex_string_to_byte", "[fast]") {
110 using namespace nitrokey::misc;
111 std::vector<uint8_t> v;
112 REQUIRE_NOTHROW(v = hex_string_to_byte("00112233445566"));
113 const uint8_t test_data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
114 REQUIRE(v.size() == sizeof(test_data));
115 for (int i = 0; i < v.size(); ++i) {
116 INFO("Position i: " << i);
117 REQUIRE(v.data()[i] == test_data[i]);
118 }
119 }
120
121 #include "test_command_ids_header.h"
122 TEST_CASE("Test device commands ids", "[fast]") {
123 // Make sure CommandID values are in sync with firmware's header
124
125 // REQUIRE(STICK20_CMD_START_VALUE == static_cast<uint8_t>(CommandID::START_VALUE));
126 REQUIRE(STICK20_CMD_ENABLE_CRYPTED_PARI == static_cast<uint8_t>(CommandID::ENABLE_CRYPTED_PARI));
127 REQUIRE(STICK20_CMD_DISABLE_CRYPTED_PARI == static_cast<uint8_t>(CommandID::DISABLE_CRYPTED_PARI));
128 REQUIRE(STICK20_CMD_ENABLE_HIDDEN_CRYPTED_PARI == static_cast<uint8_t>(CommandID::ENABLE_HIDDEN_CRYPTED_PARI));
129 REQUIRE(STICK20_CMD_DISABLE_HIDDEN_CRYPTED_PARI == static_cast<uint8_t>(CommandID::DISABLE_HIDDEN_CRYPTED_PARI));
130 REQUIRE(STICK20_CMD_ENABLE_FIRMWARE_UPDATE == static_cast<uint8_t>(CommandID::ENABLE_FIRMWARE_UPDATE));
131 REQUIRE(STICK20_CMD_EXPORT_FIRMWARE_TO_FILE == static_cast<uint8_t>(CommandID::EXPORT_FIRMWARE_TO_FILE));
132 REQUIRE(STICK20_CMD_GENERATE_NEW_KEYS == static_cast<uint8_t>(CommandID::GENERATE_NEW_KEYS));
133 REQUIRE(STICK20_CMD_FILL_SD_CARD_WITH_RANDOM_CHARS == static_cast<uint8_t>(CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS));
134
135 REQUIRE(STICK20_CMD_WRITE_STATUS_DATA == static_cast<uint8_t>(CommandID::WRITE_STATUS_DATA));
136 REQUIRE(STICK20_CMD_ENABLE_READONLY_UNCRYPTED_LUN == static_cast<uint8_t>(CommandID::ENABLE_READONLY_UNCRYPTED_LUN));
137 REQUIRE(STICK20_CMD_ENABLE_READWRITE_UNCRYPTED_LUN == static_cast<uint8_t>(CommandID::ENABLE_READWRITE_UNCRYPTED_LUN));
138
139 REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX == static_cast<uint8_t>(CommandID::SEND_PASSWORD_MATRIX));
140 REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX_PINDATA == static_cast<uint8_t>(CommandID::SEND_PASSWORD_MATRIX_PINDATA));
141 REQUIRE(STICK20_CMD_SEND_PASSWORD_MATRIX_SETUP == static_cast<uint8_t>(CommandID::SEND_PASSWORD_MATRIX_SETUP));
142
143 REQUIRE(STICK20_CMD_GET_DEVICE_STATUS == static_cast<uint8_t>(CommandID::GET_DEVICE_STATUS));
144 REQUIRE(STICK20_CMD_SEND_DEVICE_STATUS == static_cast<uint8_t>(CommandID::SEND_DEVICE_STATUS));
145
146 REQUIRE(STICK20_CMD_SEND_HIDDEN_VOLUME_PASSWORD == static_cast<uint8_t>(CommandID::SEND_HIDDEN_VOLUME_PASSWORD));
147 REQUIRE(STICK20_CMD_SEND_HIDDEN_VOLUME_SETUP == static_cast<uint8_t>(CommandID::SEND_HIDDEN_VOLUME_SETUP));
148 REQUIRE(STICK20_CMD_SEND_PASSWORD == static_cast<uint8_t>(CommandID::SEND_PASSWORD));
149 REQUIRE(STICK20_CMD_SEND_NEW_PASSWORD == static_cast<uint8_t>(CommandID::SEND_NEW_PASSWORD));
150 REQUIRE(STICK20_CMD_CLEAR_NEW_SD_CARD_FOUND == static_cast<uint8_t>(CommandID::CLEAR_NEW_SD_CARD_FOUND));
151
152 REQUIRE(STICK20_CMD_SEND_STARTUP == static_cast<uint8_t>(CommandID::SEND_STARTUP));
153 REQUIRE(STICK20_CMD_SEND_CLEAR_STICK_KEYS_NOT_INITIATED == static_cast<uint8_t>(CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED));
154 REQUIRE(STICK20_CMD_SEND_LOCK_STICK_HARDWARE == static_cast<uint8_t>(CommandID::SEND_LOCK_STICK_HARDWARE));
155
156 REQUIRE(STICK20_CMD_PRODUCTION_TEST == static_cast<uint8_t>(CommandID::PRODUCTION_TEST));
157 REQUIRE(STICK20_CMD_SEND_DEBUG_DATA == static_cast<uint8_t>(CommandID::SEND_DEBUG_DATA));
158
159 REQUIRE(STICK20_CMD_CHANGE_UPDATE_PIN == static_cast<uint8_t>(CommandID::CHANGE_UPDATE_PIN));
160
161 }
0 """
1 Copyright (c) 2015-2018 Nitrokey UG
2
3 This file is part of libnitrokey.
4
5 libnitrokey is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 any later version.
9
10 libnitrokey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: LGPL-3.0
19 """
20
21 import pytest
22
23 from conftest import skip_if_device_version_lower_than
24 from constants import DefaultPasswords, DeviceErrorCode, RFC_SECRET, bb, bbRFC_SECRET
25 from misc import ffi, gs, wait, cast_pointer_to_tuple
26 from misc import is_pro_rtm_07, is_pro_rtm_08, is_storage
27
28 @pytest.mark.lock_device
29 @pytest.mark.PWS
30 def test_enable_password_safe(C):
31 """
32 All Password Safe tests depend on AES keys being initialized. They will fail otherwise.
33 """
34 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
35 assert C.NK_enable_password_safe(b'wrong_password') == DeviceErrorCode.WRONG_PASSWORD
36 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
37
38 @pytest.mark.lock_device
39 @pytest.mark.PWS
40 def test_write_password_safe_slot(C):
41 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
42 assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_NOT_AUTHORIZED
43 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
44 assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK
45
46
47 @pytest.mark.lock_device
48 @pytest.mark.PWS
49 @pytest.mark.slowtest
50 def test_write_all_password_safe_slots_and_read_10_times(C):
51 def fill(s, wid):
52 assert wid >= len(s)
53 numbers = '1234567890'*4
54 s += numbers[:wid-len(s)]
55 assert len(s) == wid
56 return bb(s)
57
58 def get_pass(suffix):
59 return fill('pass' + suffix, 20)
60
61 def get_loginname(suffix):
62 return fill('login' + suffix, 32)
63
64 def get_slotname(suffix):
65 return fill('slotname' + suffix, 11)
66
67 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
68 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
69 PWS_slot_count = 16
70 for i in range(0, PWS_slot_count):
71 iss = str(i)
72 assert C.NK_write_password_safe_slot(i,
73 get_slotname(iss), get_loginname(iss),
74 get_pass(iss)) == DeviceErrorCode.STATUS_OK
75
76 for j in range(0, 10):
77 for i in range(0, PWS_slot_count):
78 iss = str(i)
79 assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss)
80 assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss)
81 assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss)
82
83
84 @pytest.mark.lock_device
85 @pytest.mark.PWS
86 @pytest.mark.slowtest
87 @pytest.mark.xfail(reason="This test should be run directly after test_write_all_password_safe_slots_and_read_10_times")
88 def test_read_all_password_safe_slots_10_times(C):
89 def fill(s, wid):
90 assert wid >= len(s)
91 numbers = '1234567890'*4
92 s += numbers[:wid-len(s)]
93 assert len(s) == wid
94 return bb(s)
95
96 def get_pass(suffix):
97 return fill('pass' + suffix, 20)
98
99 def get_loginname(suffix):
100 return fill('login' + suffix, 32)
101
102 def get_slotname(suffix):
103 return fill('slotname' + suffix, 11)
104
105 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
106 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
107 PWS_slot_count = 16
108
109 for j in range(0, 10):
110 for i in range(0, PWS_slot_count):
111 iss = str(i)
112 assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss)
113 assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss)
114 assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss)
115
116
117 @pytest.mark.lock_device
118 @pytest.mark.PWS
119 def test_get_password_safe_slot_name(C):
120 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
121 assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK
122 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
123 assert gs(C.NK_get_password_safe_slot_name(0)) == b''
124 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_NOT_AUTHORIZED
125
126 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
127 assert gs(C.NK_get_password_safe_slot_name(0)) == b'slotname1'
128 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
129
130
131 @pytest.mark.PWS
132 def test_get_password_safe_slot_login_password(C):
133 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
134 assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK
135 slot_login = C.NK_get_password_safe_slot_login(0)
136 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
137 assert gs(slot_login) == b'login1'
138 slot_password = gs(C.NK_get_password_safe_slot_password(0))
139 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
140 assert slot_password == b'pass1'
141
142
143 @pytest.mark.PWS
144 def test_erase_password_safe_slot(C):
145 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
146 assert C.NK_erase_password_safe_slot(0) == DeviceErrorCode.STATUS_OK
147 assert gs(C.NK_get_password_safe_slot_name(0)) == b''
148 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK # TODO CHECK shouldn't this be DeviceErrorCode.NOT_PROGRAMMED ?
149
150
151 @pytest.mark.PWS
152 def test_password_safe_slot_status(C):
153 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
154 assert C.NK_erase_password_safe_slot(0) == DeviceErrorCode.STATUS_OK
155 assert C.NK_write_password_safe_slot(1, b'slotname2', b'login2', b'pass2') == DeviceErrorCode.STATUS_OK
156 safe_slot_status = C.NK_get_password_safe_slot_status()
157 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
158 is_slot_programmed = list(ffi.cast("uint8_t [16]", safe_slot_status)[0:16])
159 print((is_slot_programmed, len(is_slot_programmed)))
160 assert is_slot_programmed[0] == 0
161 assert is_slot_programmed[1] == 1
162
163
164 @pytest.mark.aes
165 def test_issue_device_locks_on_second_key_generation_in_sequence(C):
166 if is_pro_rtm_07(C) or is_pro_rtm_08(C):
167 pytest.skip("issue to register: device locks up "
168 "after below commands sequence (reinsertion fixes), skipping for now")
169 assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
170 assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
171
172
173 @pytest.mark.aes
174 def test_regenerate_aes_key(C):
175 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
176 assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
177 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
178
179
180 @pytest.mark.lock_device
181 @pytest.mark.aes
182 @pytest.mark.factory_reset
183 def test_enable_password_safe_after_factory_reset(C):
184 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
185 if is_storage(C):
186 # for some reason storage likes to be authenticated before reset (to investigate)
187 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
188 assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
189 wait(10)
190 if is_storage(C):
191 assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
192 enable_password_safe_result = C.NK_enable_password_safe(DefaultPasswords.USER)
193 assert enable_password_safe_result == DeviceErrorCode.STATUS_AES_DEC_FAILED \
194 or is_storage(C) and enable_password_safe_result == DeviceErrorCode.WRONG_PASSWORD
195 assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
196 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
197
198 @pytest.mark.lock_device
199 @pytest.mark.aes
200 @pytest.mark.xfail(reason="NK Pro firmware bug: regenerating AES key command not always results in cleared slot data")
201 def test_destroy_password_safe(C):
202 """
203 Sometimes fails on NK Pro - slot name is not cleared ergo key generation has not succeed despite the success result
204 returned from the device
205 """
206 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
207 # write password safe slot
208 assert C.NK_write_password_safe_slot(0, b'slotname1', b'login1', b'pass1') == DeviceErrorCode.STATUS_OK
209 # read slot
210 assert gs(C.NK_get_password_safe_slot_name(0)) == b'slotname1'
211 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
212 slot_login = C.NK_get_password_safe_slot_login(0)
213 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
214 assert gs(slot_login) == b'login1'
215 # destroy password safe by regenerating aes key
216 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
217
218 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
219 assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
220 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
221 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
222
223 assert gs(C.NK_get_password_safe_slot_name(0)) != b'slotname1'
224 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
225
226 # check was slot status cleared
227 safe_slot_status = C.NK_get_password_safe_slot_status()
228 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
229 is_slot_programmed = list(ffi.cast("uint8_t [16]", safe_slot_status)[0:16])
230 assert is_slot_programmed[0] == 0
231
232
233 @pytest.mark.aes
234 def test_is_AES_supported(C):
235 if is_storage(C):
236 pytest.skip("Storage does not implement this command")
237 assert C.NK_is_AES_supported(b'wrong password') != 1
238 assert C.NK_get_last_command_status() == DeviceErrorCode.WRONG_PASSWORD
239 assert C.NK_is_AES_supported(DefaultPasswords.USER) == 1
240 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
241
242
243 @pytest.mark.pin
244 def test_admin_PIN_change(C):
245 new_password = b'123123123'
246 assert C.NK_change_admin_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD
247 assert C.NK_change_admin_PIN(DefaultPasswords.ADMIN, new_password) == DeviceErrorCode.STATUS_OK
248 assert C.NK_change_admin_PIN(new_password, DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
249
250
251 @pytest.mark.pin
252 def test_user_PIN_change(C):
253 new_password = b'123123123'
254 assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD
255 assert C.NK_change_user_PIN(DefaultPasswords.USER, new_password) == DeviceErrorCode.STATUS_OK
256 assert C.NK_change_user_PIN(new_password, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
257
258
259 @pytest.mark.lock_device
260 @pytest.mark.pin
261 def test_admin_retry_counts(C):
262 default_admin_retry_count = 3
263 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
264 assert C.NK_get_admin_retry_count() == default_admin_retry_count
265 assert C.NK_change_admin_PIN(b'wrong_password', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.WRONG_PASSWORD
266 assert C.NK_get_admin_retry_count() == default_admin_retry_count - 1
267 assert C.NK_change_admin_PIN(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
268 assert C.NK_get_admin_retry_count() == default_admin_retry_count
269
270
271 @pytest.mark.lock_device
272 @pytest.mark.pin
273 def test_user_retry_counts_change_PIN(C):
274 assert C.NK_change_user_PIN(DefaultPasswords.USER, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
275 wrong_password = b'wrong_password'
276 default_user_retry_count = 3
277 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
278 assert C.NK_get_user_retry_count() == default_user_retry_count
279 assert C.NK_change_user_PIN(wrong_password, wrong_password) == DeviceErrorCode.WRONG_PASSWORD
280 assert C.NK_get_user_retry_count() == default_user_retry_count - 1
281 assert C.NK_change_user_PIN(DefaultPasswords.USER, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
282 assert C.NK_get_user_retry_count() == default_user_retry_count
283
284
285 @pytest.mark.lock_device
286 @pytest.mark.pin
287 def test_user_retry_counts_PWSafe(C):
288 default_user_retry_count = 3
289 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
290 assert C.NK_get_user_retry_count() == default_user_retry_count
291 assert C.NK_enable_password_safe(b'wrong_password') == DeviceErrorCode.WRONG_PASSWORD
292 assert C.NK_get_user_retry_count() == default_user_retry_count - 1
293 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
294 assert C.NK_get_user_retry_count() == default_user_retry_count
295
296
297 @pytest.mark.pin
298 def test_unlock_user_password(C):
299 default_user_retry_count = 3
300 default_admin_retry_count = 3
301 new_password = b'123123123'
302 assert C.NK_get_user_retry_count() == default_user_retry_count
303 assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD
304 assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD
305 assert C.NK_change_user_PIN(b'wrong_password', new_password) == DeviceErrorCode.WRONG_PASSWORD
306 assert C.NK_get_user_retry_count() == default_user_retry_count - 3
307 assert C.NK_get_admin_retry_count() == default_admin_retry_count
308
309 assert C.NK_unlock_user_password(b'wrong password', DefaultPasswords.USER) == DeviceErrorCode.WRONG_PASSWORD
310 assert C.NK_get_admin_retry_count() == default_admin_retry_count - 1
311 assert C.NK_unlock_user_password(DefaultPasswords.ADMIN, DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
312 assert C.NK_get_user_retry_count() == default_user_retry_count
313 assert C.NK_get_admin_retry_count() == default_admin_retry_count
314
315
316 @pytest.mark.pin
317 def test_admin_auth(C):
318 assert C.NK_first_authenticate(b'wrong_password', DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.WRONG_PASSWORD
319 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
320
321
322 @pytest.mark.pin
323 def test_user_auth(C):
324 assert C.NK_user_authenticate(b'wrong_password', DefaultPasswords.USER_TEMP) == DeviceErrorCode.WRONG_PASSWORD
325 assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK
326
327
328 @pytest.mark.otp
329 def check_HOTP_RFC_codes(C, func, prep=None, use_8_digits=False):
330 """
331 # https://tools.ietf.org/html/rfc4226#page-32
332 """
333 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
334 assert C.NK_write_hotp_slot(1, b'python_test', bbRFC_SECRET, 0, use_8_digits, False, False, b'',
335 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
336 test_data = [
337 1284755224, 1094287082, 137359152, 1726969429, 1640338314, 868254676, 1918287922, 82162583, 673399871,
338 645520489,
339 ]
340 for code in test_data:
341 if prep:
342 prep()
343 r = func(1)
344 code = str(code)[-8:] if use_8_digits else str(code)[-6:]
345 assert bb(code) == r
346
347
348 @pytest.mark.otp
349 @pytest.mark.parametrize("use_8_digits", [False, True, ])
350 @pytest.mark.parametrize("use_pin_protection", [False, True, ])
351 def test_HOTP_RFC_use8digits_usepin(C, use_8_digits, use_pin_protection):
352 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
353 assert C.NK_write_config(255, 255, 255, use_pin_protection, not use_pin_protection,
354 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
355 if use_pin_protection:
356 check_HOTP_RFC_codes(C,
357 lambda x: gs(C.NK_get_hotp_code_PIN(x, DefaultPasswords.USER_TEMP)),
358 lambda: C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP),
359 use_8_digits=use_8_digits)
360 else:
361 check_HOTP_RFC_codes(C, lambda x: gs(C.NK_get_hotp_code(x)), use_8_digits=use_8_digits)
362
363
364 @pytest.mark.otp
365 def test_HOTP_token(C):
366 """
367 Check HOTP routine with written token ID to slot.
368 """
369 use_pin_protection = False
370 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
371 assert C.NK_write_config(255, 255, 255, use_pin_protection, not use_pin_protection,
372 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
373 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
374 token_ID = b"AAV100000022"
375 assert C.NK_write_hotp_slot(1, b'python_test', bbRFC_SECRET, 0, False, False, True, token_ID,
376 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
377 for i in range(5):
378 hotp_code = gs(C.NK_get_hotp_code(1))
379 assert hotp_code != b''
380 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
381
382
383 @pytest.mark.otp
384 def test_HOTP_counters(C):
385 """
386 # https://tools.ietf.org/html/rfc4226#page-32
387 """
388 use_pin_protection = False
389 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
390 assert C.NK_write_config(255, 255, 255, use_pin_protection, not use_pin_protection,
391 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
392 use_8_digits = True
393 HOTP_test_data = [
394 1284755224, 1094287082, 137359152, 1726969429, 1640338314,
395 868254676, 1918287922, 82162583, 673399871, 645520489,
396 ]
397 slot_number = 1
398 for counter, code in enumerate(HOTP_test_data):
399 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
400 assert C.NK_write_hotp_slot(slot_number, b'python_test', bbRFC_SECRET, counter, use_8_digits, False, False, b'',
401 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
402 r = gs(C.NK_get_hotp_code(slot_number))
403 code = str(code)[-8:] if use_8_digits else str(code)[-6:]
404 assert bb(code) == r
405
406
407 INT32_MAX = 2 ** 31 - 1
408 @pytest.mark.otp
409 def test_HOTP_64bit_counter(C):
410 if is_storage(C):
411 pytest.xfail('bug in NK Storage HOTP firmware - counter is set with a 8 digits string, '
412 'however int32max takes 10 digits to be written')
413 oath = pytest.importorskip("oath")
414 lib_at = lambda t: bb(oath.hotp(RFC_SECRET, t, format='dec6'))
415 PIN_protection = False
416 use_8_digits = False
417 slot_number = 1
418 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
419 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
420 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
421 dev_res = []
422 lib_res = []
423 for t in range(INT32_MAX - 5, INT32_MAX + 5, 1):
424 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
425 assert C.NK_write_hotp_slot(slot_number, b'python_test', bbRFC_SECRET, t, use_8_digits, False, False, b'',
426 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
427 code_device = gs(C.NK_get_hotp_code(slot_number))
428 dev_res += (t, code_device)
429 lib_res += (t, lib_at(t))
430 assert dev_res == lib_res
431
432
433 @pytest.mark.otp
434 def test_TOTP_64bit_time(C):
435 if is_storage(C):
436 pytest.xfail('bug in NK Storage TOTP firmware')
437 oath = pytest.importorskip("oath")
438 T = 1
439 lib_at = lambda t: bb(oath.totp(RFC_SECRET, t=t))
440 PIN_protection = False
441 slot_number = 1
442 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
443 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
444 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
445 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
446 assert C.NK_write_totp_slot(slot_number, b'python_test', bbRFC_SECRET, 30, False, False, False, b'',
447 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
448 dev_res = []
449 lib_res = []
450 for t in range(INT32_MAX - 5, INT32_MAX + 5, 1):
451 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
452 assert C.NK_totp_set_time(t) == DeviceErrorCode.STATUS_OK
453 code_device = gs((C.NK_get_totp_code(slot_number, T, 0, 30)))
454 dev_res += (t, code_device)
455 lib_res += (t, lib_at(t))
456 assert dev_res == lib_res
457
458
459 @pytest.mark.otp
460 @pytest.mark.xfail(reason="NK Pro: Test fails in 50% of cases due to test vectors set 1 second before interval count change"
461 "Here time is changed on seconds side only and miliseconds part is not being reset apparently"
462 "This results in available time to test of half a second on average, thus 50% failed cases"
463 "With disabled two first test vectors test passess 10/10 times"
464 "Fail may also occurs on NK Storage with lower occurrency since it needs less time to execute "
465 "commands")
466 @pytest.mark.parametrize("PIN_protection", [False, True, ])
467 def test_TOTP_RFC_usepin(C, PIN_protection):
468 slot_number = 1
469 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
470 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
471 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
472 # test according to https://tools.ietf.org/html/rfc6238#appendix-B
473 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
474 assert C.NK_write_totp_slot(slot_number, b'python_test', bbRFC_SECRET, 30, True, False, False, b'',
475 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
476
477 get_func = None
478 if PIN_protection:
479 get_func = lambda x, y, z, r: gs(C.NK_get_totp_code_PIN(x, y, z, r, DefaultPasswords.USER_TEMP))
480 else:
481 get_func = lambda x, y, z, r: gs(C.NK_get_totp_code(x, y, z, r))
482
483 # Mode: Sha1, time step X=30
484 test_data = [
485 #Time T (hex) TOTP
486 (59, 0x1, 94287082), # Warning - test vector time 1 second before interval count changes
487 (1111111109, 0x00000000023523EC, 7081804), # Warning - test vector time 1 second before interval count changes
488 (1111111111, 0x00000000023523ED, 14050471),
489 (1234567890, 0x000000000273EF07, 89005924),
490 (2000000000, 0x0000000003F940AA, 69279037),
491 # (20000000000, 0x0000000027BC86AA, 65353130), # 64bit is also checked in other test
492 ]
493 responses = []
494 data = []
495 correct = 0
496 for t, T, expected_code in test_data:
497 if PIN_protection:
498 C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP)
499 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
500 assert C.NK_totp_set_time(t) == DeviceErrorCode.STATUS_OK
501 code_from_device = get_func(slot_number, T, 0, 30) # FIXME T is not changing the outcome
502 data += [ (t, bb(str(expected_code).zfill(8))) ]
503 responses += [ (t, code_from_device) ]
504 correct += expected_code == code_from_device
505 assert data == responses or correct == len(test_data)
506
507
508 @pytest.mark.otp
509 def test_get_slot_names(C):
510 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
511 assert C.NK_erase_totp_slot(0, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
512 # erasing slot invalidates temporary password, so requesting authentication
513 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
514 assert C.NK_erase_hotp_slot(0, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
515
516 for i in range(15):
517 name = ffi.string(C.NK_get_totp_slot_name(i))
518 if name == '':
519 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
520 for i in range(3):
521 name = ffi.string(C.NK_get_hotp_slot_name(i))
522 if name == '':
523 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
524
525
526 @pytest.mark.otp
527 def test_get_OTP_codes(C):
528 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
529 assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
530 for i in range(15):
531 code = gs(C.NK_get_totp_code(i, 0, 0, 0))
532 if code == b'':
533 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
534
535 for i in range(3):
536 code = gs(C.NK_get_hotp_code(i))
537 if code == b'':
538 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
539
540
541 @pytest.mark.otp
542 def test_get_OTP_code_from_not_programmed_slot(C):
543 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
544 assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
545 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
546 assert C.NK_erase_hotp_slot(0, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
547 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
548 assert C.NK_erase_totp_slot(0, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
549
550 code = gs(C.NK_get_hotp_code(0))
551 assert code == b''
552 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
553
554 code = gs(C.NK_get_totp_code(0, 0, 0, 0))
555 assert code == b''
556 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
557
558
559 @pytest.mark.otp
560 def test_get_code_user_authorize(C):
561 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
562 assert C.NK_write_totp_slot(0, b'python_otp_auth', bbRFC_SECRET, 30, True, False, False, b'',
563 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
564 # enable PIN protection of OTP codes with write_config
565 # TODO create convinience function on C API side to enable/disable OTP USER_PIN protection
566 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
567 assert C.NK_write_config(255, 255, 255, True, False, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
568 code = gs(C.NK_get_totp_code(0, 0, 0, 0))
569 assert code == b''
570 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_NOT_AUTHORIZED
571 # disable PIN protection with write_config
572 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
573 assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
574 code = gs(C.NK_get_totp_code(0, 0, 0, 0))
575 assert code != b''
576 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
577
578
579 def cast_pointer_to_tuple(obj, typen, len):
580 # usage:
581 # config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5)
582 return tuple(ffi.cast("%s [%d]" % (typen, len), obj)[0:len])
583
584
585 def test_read_write_config(C):
586 # let's set sample config with pin protection and disabled scrolllock
587 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
588 assert C.NK_write_config(0, 1, 2, True, False, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
589 config_raw_data = C.NK_read_config()
590 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
591 config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5)
592 assert config == (0, 1, 2, True, False)
593
594 # restore defaults and check
595 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
596 assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
597 config_raw_data = C.NK_read_config()
598 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
599 config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5)
600 assert config == (255, 255, 255, False, True)
601
602
603 @pytest.mark.lock_device
604 @pytest.mark.factory_reset
605 def test_factory_reset(C):
606 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
607 assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
608 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
609 assert C.NK_write_hotp_slot(1, b'python_test', bbRFC_SECRET, 0, False, False, False, b"",
610 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
611 assert gs(C.NK_get_hotp_code(1)) == b"755224"
612 assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
613 wait(10)
614 assert gs(C.NK_get_hotp_code(1)) != b"287082"
615 assert C.NK_get_last_command_status() == DeviceErrorCode.NOT_PROGRAMMED
616 # restore AES key
617 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
618 assert C.NK_build_aes_key(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
619 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
620 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
621 if is_storage(C):
622 assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
623
624
625 @pytest.mark.status
626 def test_get_status(C):
627 status = C.NK_status()
628 s = gs(status)
629 assert len(s) > 0
630
631 @pytest.mark.status
632 def test_get_serial_number(C):
633 sn = C.NK_device_serial_number()
634 sn = gs(sn)
635 assert len(sn) > 0
636 print(('Serial number of the device: ', sn))
637
638
639 @pytest.mark.otp
640 @pytest.mark.parametrize("secret", ['000001', '00'*10+'ff', '00'*19+'ff', '000102',
641 '00'*29+'ff', '00'*39+'ff', '002EF43F51AFA97BA2B46418768123C9E1809A5B' ])
642 def test_OTP_secret_started_from_null(C, secret):
643 """
644 NK Pro 0.8+, NK Storage 0.43+
645 """
646 skip_if_device_version_lower_than({'S': 43, 'P': 8})
647 if len(secret) > 40:
648 # feature: 320 bit long secret handling
649 skip_if_device_version_lower_than({'P': 8})
650
651 oath = pytest.importorskip("oath")
652 lib_at = lambda t: bb(oath.hotp(secret, t, format='dec6'))
653 PIN_protection = False
654 use_8_digits = False
655 slot_number = 1
656 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
657 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
658 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
659 dev_res = []
660 lib_res = []
661 for t in range(1,5):
662 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
663 assert C.NK_write_hotp_slot(slot_number, b'null_secret', bb(secret), t, use_8_digits, False, False, b'',
664 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
665 code_device = gs(C.NK_get_hotp_code(slot_number))
666 dev_res += (t, code_device)
667 lib_res += (t, lib_at(t))
668 assert dev_res == lib_res
669
670
671 @pytest.mark.otp
672 @pytest.mark.parametrize("counter", [0, 3, 7, 0xffff,
673 0xffffffff,
674 0xffffffffffffffff] )
675 def test_HOTP_slots_read_write_counter(C, counter):
676 """
677 Write different counters to all HOTP slots, read code and compare with 3rd party
678 :param counter:
679 """
680 if counter >= 1e7:
681 # Storage does not handle counters longer than 7 digits
682 skip_if_device_version_lower_than({'P': 7})
683
684 secret = RFC_SECRET
685 oath = pytest.importorskip("oath")
686 lib_at = lambda t: bb(oath.hotp(secret, t, format='dec6'))
687 PIN_protection = False
688 use_8_digits = False
689 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
690 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
691 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
692 dev_res = []
693 lib_res = []
694 for slot_number in range(3):
695 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
696 assert C.NK_write_hotp_slot(slot_number, b'HOTP rw' + bytes(slot_number), bb(secret), counter, use_8_digits, False, False, b"",
697 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
698 code_device = gs(C.NK_get_hotp_code(slot_number))
699 dev_res += (counter, code_device)
700 lib_res += (counter, lib_at(counter))
701 assert dev_res == lib_res
702
703
704 @pytest.mark.otp
705 @pytest.mark.parametrize("period", [30,60] )
706 @pytest.mark.parametrize("time", range(21,70,20) )
707 def test_TOTP_slots_read_write_at_time_period(C, time, period):
708 """
709 Write to all TOTP slots with specified period, read code at specified time
710 and compare with 3rd party
711 """
712 secret = RFC_SECRET
713 oath = pytest.importorskip("oath")
714 lib_at = lambda t: bb(oath.totp(RFC_SECRET, t=t, period=period))
715 PIN_protection = False
716 use_8_digits = False
717 T = 0
718 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
719 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
720 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
721 dev_res = []
722 lib_res = []
723 for slot_number in range(15):
724 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
725 assert C.NK_write_totp_slot(slot_number, b'TOTP rw' + bytes(slot_number), bb(secret), period, use_8_digits, False, False, b"",
726 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
727 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
728 assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK
729 code_device = gs(C.NK_get_totp_code(slot_number, T, 0, period))
730 dev_res += (time, code_device)
731 lib_res += (time, lib_at(time))
732 assert dev_res == lib_res
733
734
735 @pytest.mark.otp
736 @pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] )
737 def test_TOTP_secrets(C, secret):
738 '''
739 NK Pro 0.8+, NK Storage 0.44+
740 '''
741 skip_if_device_version_lower_than({'S': 44, 'P': 8})
742
743 if len(secret)>20*2: #*2 since secret is in hex
744 # pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 and NK Storage")
745 skip_if_device_version_lower_than({'P': 8})
746 slot_number = 0
747 time = 0
748 period = 30
749 oath = pytest.importorskip("oath")
750 lib_at = lambda t: bb(oath.totp(secret, t=t, period=period))
751 PIN_protection = False
752 use_8_digits = False
753 T = 0
754 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
755 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
756 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
757 dev_res = []
758 lib_res = []
759 assert C.NK_write_totp_slot(slot_number, b'secret' + bytes(len(secret)), bb(secret), period, use_8_digits, False, False, b"",
760 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
761 assert C.NK_totp_set_time(time) == DeviceErrorCode.STATUS_OK
762 code_device = gs(C.NK_get_totp_code(slot_number, T, 0, period))
763 dev_res += (time, code_device)
764 lib_res += (time, lib_at(time))
765 assert dev_res == lib_res
766
767
768 @pytest.mark.otp
769 @pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] )
770 def test_HOTP_secrets(C, secret):
771 """
772 NK Pro 0.8+
773 feature needed: support for 320bit secrets
774 """
775 if len(secret)>40:
776 skip_if_device_version_lower_than({'P': 8})
777
778 slot_number = 0
779 counter = 0
780 oath = pytest.importorskip("oath")
781 lib_at = lambda t: bb(oath.hotp(secret, counter=t))
782 PIN_protection = False
783 use_8_digits = False
784 T = 0
785 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
786 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
787 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
788 dev_res = []
789 lib_res = []
790 # repeat authentication for Pro 0.7
791 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
792 assert C.NK_write_hotp_slot(slot_number, b'secret' + bytes(len(secret)), bb(secret), counter, use_8_digits, False, False, b"",
793 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
794 code_device = gs(C.NK_get_hotp_code(slot_number))
795 dev_res += (counter, code_device)
796 lib_res += (counter, lib_at(counter))
797 assert dev_res == lib_res
798
799
800 def test_special_double_press(C):
801 """
802 requires manual check after function run
803 double press each of num-, scroll-, caps-lock and check inserted OTP codes (each 1st should be 755224)
804 on nkpro 0.7 scrolllock should do nothing, on nkpro 0.8+ should return OTP code
805 """
806 secret = RFC_SECRET
807 counter = 0
808 PIN_protection = False
809 use_8_digits = False
810 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
811 assert C.NK_write_config(0, 1, 2, PIN_protection, not PIN_protection,
812 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
813 for slot_number in range(3):
814 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
815 assert C.NK_write_hotp_slot(slot_number, b'double' + bytes(slot_number), bb(secret), counter, use_8_digits, False, False, b"",
816 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
817 # requires manual check
818
819
820 @pytest.mark.otp
821 def test_edit_OTP_slot(C):
822 """
823 should change slots counter and name without changing its secret (using null secret for second update)
824 """
825 # counter is not getting updated under Storage v0.43 - #TOREPORT
826 skip_if_device_version_lower_than({'S': 44, 'P': 7})
827
828 secret = RFC_SECRET
829 counter = 0
830 PIN_protection = False
831 use_8_digits = False
832 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
833 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
834 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
835 slot_number = 0
836 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
837 first_name = b'edit slot'
838 assert C.NK_write_hotp_slot(slot_number, first_name, bb(secret), counter, use_8_digits, False, False, b"",
839 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
840 assert gs(C.NK_get_hotp_slot_name(slot_number)) == first_name
841
842
843 first_code = gs(C.NK_get_hotp_code(slot_number))
844 changed_name = b'changedname'
845 empty_secret = b''
846 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
847 assert C.NK_write_hotp_slot(slot_number, changed_name, empty_secret, counter, use_8_digits, False, False, b"",
848 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
849 second_code = gs(C.NK_get_hotp_code(slot_number))
850 assert first_code == second_code
851 assert gs(C.NK_get_hotp_slot_name(slot_number)) == changed_name
852
853
854 @pytest.mark.otp
855 @pytest.mark.skip
856 @pytest.mark.parametrize("secret", ['31323334353637383930'*2,'31323334353637383930'*4] )
857 def test_TOTP_codes_from_nitrokeyapp(secret, C):
858 """
859 Helper test for manual TOTP check of written secret by Nitrokey App
860 Destined to run by hand
861 """
862 slot_number = 0
863 PIN_protection = False
864 period = 30
865 assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
866 assert C.NK_write_config(255, 255, 255, PIN_protection, not PIN_protection,
867 DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
868 code_device = gs(C.NK_get_totp_code(slot_number, 0, 0, period))
869
870 oath = pytest.importorskip("oath")
871 lib_at = lambda : bb(oath.totp(secret, period=period))
872 print (lib_at())
873 assert lib_at() == code_device
0 """
1 Copyright (c) 2015-2018 Nitrokey UG
2
3 This file is part of libnitrokey.
4
5 libnitrokey is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 any later version.
9
10 libnitrokey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: LGPL-3.0
19 """
20
21 import pprint
22 import pytest
23
24 from conftest import skip_if_device_version_lower_than
25 from constants import DefaultPasswords, DeviceErrorCode, bb
26 from misc import gs, wait
27 pprint = pprint.PrettyPrinter(indent=4).pprint
28
29
30 def get_dict_from_dissect(status):
31 x = []
32 for s in status.split('\n'):
33 try:
34 if not ':' in s: continue
35 ss = s.replace('\t', '').replace(' (int) ', '').split(':')
36 if not len(ss) == 2: continue
37 x.append(ss)
38 except:
39 pass
40 d = {k.strip(): v.strip() for k, v in x}
41 return d
42
43
44 @pytest.mark.other
45 @pytest.mark.info
46 def test_get_status_storage(C):
47 skip_if_device_version_lower_than({'S': 43})
48 status_pointer = C.NK_get_status_storage_as_string()
49 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
50 status_string = gs(status_pointer)
51 assert len(status_string) > 0
52 status_dict = get_dict_from_dissect(status_string.decode('ascii'))
53 default_admin_password_retry_count = 3
54 assert int(status_dict['AdminPwRetryCount']) == default_admin_password_retry_count
55
56
57 @pytest.mark.other
58 @pytest.mark.info
59 def test_sd_card_usage(C):
60 skip_if_device_version_lower_than({'S': 43})
61 data_pointer = C.NK_get_SD_usage_data_as_string()
62 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
63 data_string = gs(data_pointer)
64 assert len(data_string) > 0
65 data_dict = get_dict_from_dissect(data_string.decode("ascii"))
66 assert int(data_dict['WriteLevelMax']) <= 100
67
68
69 @pytest.mark.encrypted
70 def test_encrypted_volume_unlock(C):
71 skip_if_device_version_lower_than({'S': 43})
72 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
73 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
74
75
76 @pytest.mark.hidden
77 def test_encrypted_volume_unlock_hidden(C):
78 skip_if_device_version_lower_than({'S': 43})
79 hidden_volume_password = b'hiddenpassword'
80 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
81 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
82 assert C.NK_create_hidden_volume(0, 20, 21, hidden_volume_password) == DeviceErrorCode.STATUS_OK
83 assert C.NK_unlock_hidden_volume(hidden_volume_password) == DeviceErrorCode.STATUS_OK
84
85
86 @pytest.mark.hidden
87 def test_encrypted_volume_setup_multiple_hidden_lock(C):
88 import random
89 skip_if_device_version_lower_than({'S': 45}) #hangs device on lower version
90 hidden_volume_password = b'hiddenpassword' + bb(str(random.randint(0,100)))
91 p = lambda i: hidden_volume_password + bb(str(i))
92 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
93 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
94 for i in range(4):
95 assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i) ) == DeviceErrorCode.STATUS_OK
96 for i in range(4):
97 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
98 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
99 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
100
101
102 @pytest.mark.hidden
103 @pytest.mark.parametrize("volumes_to_setup", range(1, 5))
104 def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes(C, volumes_to_setup):
105 skip_if_device_version_lower_than({'S': 43})
106 hidden_volume_password = b'hiddenpassword'
107 p = lambda i: hidden_volume_password + bb(str(i))
108 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
109 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
110 for i in range(volumes_to_setup):
111 assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
112
113 assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
114 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
115
116 for i in range(volumes_to_setup):
117 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
118 # TODO mount and test for files
119 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
120
121
122 @pytest.mark.hidden
123 @pytest.mark.parametrize("volumes_to_setup", range(1, 5))
124 def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes_unlock_at_once(C, volumes_to_setup):
125 skip_if_device_version_lower_than({'S': 43})
126 hidden_volume_password = b'hiddenpassword'
127 p = lambda i: hidden_volume_password + bb(str(i))
128 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
129 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
130 for i in range(volumes_to_setup):
131 assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
132 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
133 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
134
135 assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
136 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
137
138 for i in range(volumes_to_setup):
139 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
140 # TODO mount and test for files
141 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
142
143
144 @pytest.mark.hidden
145 @pytest.mark.parametrize("use_slot", range(4))
146 def test_encrypted_volume_setup_one_hidden_no_lock_device_slot(C, use_slot):
147 skip_if_device_version_lower_than({'S': 43})
148 hidden_volume_password = b'hiddenpassword'
149 p = lambda i: hidden_volume_password + bb(str(i))
150 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
151 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
152 i = use_slot
153 assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
154 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
155 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
156
157 assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
158 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
159
160 for j in range(3):
161 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
162 # TODO mount and test for files
163 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
164
165
166 @pytest.mark.hidden
167 @pytest.mark.PWS
168 def test_password_safe_slot_name_corruption(C):
169 skip_if_device_version_lower_than({'S': 43})
170 volumes_to_setup = 4
171 # connected with encrypted volumes, possible also with hidden
172 def fill(s, wid):
173 assert wid >= len(s)
174 numbers = '1234567890' * 4
175 s += numbers[:wid - len(s)]
176 assert len(s) == wid
177 return bb(s)
178
179 def get_pass(suffix):
180 return fill('pass' + suffix, 20)
181
182 def get_loginname(suffix):
183 return fill('login' + suffix, 32)
184
185 def get_slotname(suffix):
186 return fill('slotname' + suffix, 11)
187
188 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
189 assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
190 PWS_slot_count = 16
191 for i in range(0, PWS_slot_count):
192 iss = str(i)
193 assert C.NK_write_password_safe_slot(i,
194 get_slotname(iss), get_loginname(iss),
195 get_pass(iss)) == DeviceErrorCode.STATUS_OK
196
197 def check_PWS_correctness(C):
198 for i in range(0, PWS_slot_count):
199 iss = str(i)
200 assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss)
201 assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss)
202 assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss)
203
204 hidden_volume_password = b'hiddenpassword'
205 p = lambda i: hidden_volume_password + bb(str(i))
206 def check_volumes_correctness(C):
207 for i in range(volumes_to_setup):
208 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
209 # TODO mount and test for files
210 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
211
212 check_PWS_correctness(C)
213
214 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
215 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
216 for i in range(volumes_to_setup):
217 assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
218 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
219 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
220
221 assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
222 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
223
224 check_volumes_correctness(C)
225 check_PWS_correctness(C)
226 check_volumes_correctness(C)
227 check_PWS_correctness(C)
228
229 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
230 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
231 check_volumes_correctness(C)
232 check_PWS_correctness(C)
233 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
234 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
235 check_volumes_correctness(C)
236 check_PWS_correctness(C)
237
238
239 @pytest.mark.hidden
240 def test_hidden_volume_corruption(C):
241 # bug: this should return error without unlocking encrypted volume each hidden volume lock, but it does not
242 skip_if_device_version_lower_than({'S': 43})
243 hidden_volume_password = b'hiddenpassword'
244 p = lambda i: hidden_volume_password + bb(str(i))
245 volumes_to_setup = 4
246 for i in range(volumes_to_setup):
247 assert C.NK_create_hidden_volume(i, 20 + i * 10, 20 + i * 10 + i + 1, p(i)) == DeviceErrorCode.STATUS_OK
248 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
249 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
250
251 assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
252
253 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
254 for i in range(volumes_to_setup):
255 assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
256 assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
257 wait(2)
258 assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
259
260
261 @pytest.mark.unencrypted
262 def test_unencrypted_volume_set_read_only(C):
263 skip_if_device_version_lower_than({'S': 43})
264 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
265 assert C.NK_set_unencrypted_read_only(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
266
267
268 @pytest.mark.unencrypted
269 def test_unencrypted_volume_set_read_write(C):
270 skip_if_device_version_lower_than({'S': 43})
271 assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
272 assert C.NK_set_unencrypted_read_write(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
273
274
275 @pytest.mark.other
276 def test_export_firmware(C):
277 skip_if_device_version_lower_than({'S': 43})
278 assert C.NK_export_firmware(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
279
280
281 @pytest.mark.other
282 def test_clear_new_sd_card_notification(C):
283 skip_if_device_version_lower_than({'S': 43})
284 assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
285
286
287 @pytest.mark.encrypted
288 @pytest.mark.slowtest
289 @pytest.mark.skip(reason='long test (about 1h)')
290 def test_fill_SD_card(C):
291 skip_if_device_version_lower_than({'S': 43})
292 status = C.NK_fill_SD_card_with_random_data(DefaultPasswords.ADMIN)
293 assert status == DeviceErrorCode.STATUS_OK or status == DeviceErrorCode.BUSY
294 while 1:
295 value = C.NK_get_progress_bar_value()
296 if value == -1: break
297 assert 0 <= value <= 100
298 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
299 wait(5)
300
301
302 @pytest.mark.other
303 @pytest.mark.info
304 def test_get_busy_progress_on_idle(C):
305 skip_if_device_version_lower_than({'S': 43})
306 value = C.NK_get_progress_bar_value()
307 assert value == -1
308 assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
309
310
311 @pytest.mark.update
312 def test_change_update_password(C):
313 skip_if_device_version_lower_than({'S': 43})
314 wrong_password = b'aaaaaaaaaaa'
315 assert C.NK_change_update_password(wrong_password, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.WRONG_PASSWORD
316 assert C.NK_change_update_password(DefaultPasswords.UPDATE, DefaultPasswords.UPDATE_TEMP) == DeviceErrorCode.STATUS_OK
317 assert C.NK_change_update_password(DefaultPasswords.UPDATE_TEMP, DefaultPasswords.UPDATE) == DeviceErrorCode.STATUS_OK
318
319
320 @pytest.mark.other
321 def test_send_startup(C):
322 skip_if_device_version_lower_than({'S': 43})
323 time_seconds_from_epoch = 0 # FIXME set proper date
324 assert C.NK_send_startup(time_seconds_from_epoch) == DeviceErrorCode.STATUS_OK