summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzczepan Zalega <szczepan@nitrokey.com>2017-03-11 17:18:59 +0100
committerSzczepan Zalega <szczepan@nitrokey.com>2017-03-11 17:18:59 +0100
commit22d05ce647281056d71fbd3c31df3bcd6396188d (patch)
tree90208930f54c47987bfd5ffcf0a0acaaad2510da
parented5044da43172d86a1aa475473561a4818b7c69c (diff)
parentac6b9c18ef55f4cd36e85069cf0cf82c14e04404 (diff)
downloadlibnitrokey-22d05ce647281056d71fbd3c31df3bcd6396188d.tar.gz
libnitrokey-22d05ce647281056d71fbd3c31df3bcd6396188d.tar.bz2
Merge branch 'libnitrokey_3'
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules6
-rw-r--r--.idea/vcs.xml3
-rw-r--r--.travis.yml58
-rw-r--r--CMakeLists.txt123
-rw-r--r--CMakeSettings.json37
-rw-r--r--DeviceCommunicationExceptions.cpp3
-rw-r--r--Makefile45
-rw-r--r--NK_C_API.cc128
-rw-r--r--NK_C_API.h120
-rw-r--r--NitrokeyManager.cc300
-rw-r--r--device.cc201
m---------hidapi0
-rw-r--r--include/CommandFailedException.h36
-rw-r--r--include/DeviceCommunicationExceptions.h40
-rw-r--r--include/LibraryException.h6
-rw-r--r--include/LongOperationInProgressException.h2
-rw-r--r--include/NitrokeyManager.h43
-rw-r--r--include/command.h7
-rw-r--r--include/command_id.h8
-rw-r--r--include/cxx_semantics.h8
-rw-r--r--include/device.h85
-rw-r--r--include/device_proto.h132
-rw-r--r--include/dissect.h4
-rw-r--r--include/hidapi/hidapi.h391
-rw-r--r--include/log.h15
-rw-r--r--include/misc.h18
-rw-r--r--include/stick10_commands.h63
-rw-r--r--include/stick10_commands_0.8.h2
-rw-r--r--include/stick20_commands.h45
-rw-r--r--log.cc4
-rw-r--r--misc.cc34
m---------python_bindings/pybind110
-rw-r--r--unittest/Makefile33
-rw-r--r--unittest/conftest.py22
-rw-r--r--unittest/requirements.txt1
-rw-r--r--unittest/setup_python_dependencies.sh3
-rw-r--r--unittest/test.cc10
-rw-r--r--unittest/test2.cc28
-rw-r--r--unittest/test3.cc27
-rw-r--r--unittest/test_C_API.cpp4
-rw-r--r--unittest/test_HOTP.cc9
-rw-r--r--unittest/test_pro.py83
-rw-r--r--unittest/test_storage.py155
44 files changed, 1816 insertions, 527 deletions
diff --git a/.gitignore b/.gitignore
index 47b7703..c98c3a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ core
.cache/
.idea/
CMakeFiles/
+/.vs
diff --git a/.gitmodules b/.gitmodules
index 6e2a0ec..4608496 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "unittest/Catch"]
path = unittest/Catch
url = https://github.com/philsquared/Catch.git
-[submodule "pybind11"]
- path = python_bindings/pybind11
- url = https://github.com/pybind/pybind11.git
+[submodule "hidapi"]
+ path = hidapi
+ url = https://github.com/Nitrokey/hidapi.git
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7..486a99a 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,5 +2,8 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
+ <mapping directory="$PROJECT_DIR$/hidapi" vcs="Git" />
+ <mapping directory="$PROJECT_DIR$/python_bindings/pybind11" vcs="Git" />
+ <mapping directory="$PROJECT_DIR$/unittest/Catch" vcs="Git" />
</component>
</project> \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..ba4465b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,58 @@
+language: generic
+sudo: false
+os: osx
+env: CF=
+
+matrix:
+ include:
+# - osx_image: xcode7.3 #default
+# before_install: &brew
+# - brew update
+# - brew install hidapi
+ - osx_image: xcode6.4
+ - osx_image: xcode8.2
+ - os: linux
+ dist: trusty
+ env: COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 CF=-DCOMPILE_TESTS=ON
+ addons:
+ apt:
+ packages:
+ - cmake
+ - libhidapi-dev
+ - g++-5
+ sources: &sources
+ - ubuntu-toolchain-r-test
+ - os: linux
+ dist: trusty
+ env: COMPILER_NAME=gcc CXX=g++-6 CC=gcc-6 CF=-DCOMPILE_TESTS=ON
+ addons:
+ apt:
+ packages:
+ - cmake
+ - libhidapi-dev
+ - g++-6
+ sources: *sources
+ - os: linux
+ dist: trusty
+ env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8 CF=-DCOMPILE_TESTS=ON
+ addons:
+ apt:
+ packages:
+ - cmake
+ - libhidapi-dev
+ - g++-5
+ - clang-3.8
+ sources: *sources
+
+
+install:
+ - mkdir -p build
+ - cd build
+# - export CXXFLAGS="${CXX_FLAGS} -Wall -Wextra -Werror" # TODO enable when fixed
+ - ${CXX} --version || true
+ - cmake --version
+ - cmake .. ${CF}
+
+script:
+ - make -j2
+# - make test # TODO add library device-less tests \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c324067..46405a9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,14 +1,41 @@
-cmake_minimum_required(VERSION 3.5)
-project(libnitrokey)
+cmake_minimum_required(VERSION 3.1)
+IF (UNIX)
+ OPTION(USE_CLANG "Use CLang" FALSE)
+ IF(USE_CLANG)
+ set(CMAKE_CXX_COMPILER "/usr/bin/clang++" CACHE string "clang++ compiler" FORCE)
+ ELSE()
+ set(CMAKE_CXX_COMPILER)
+ ENDIF()
+ OPTION(ADD_ASAN "Use ASAN to show memory issues" FALSE)
+ OPTION(ADD_TSAN "Use TSAN to show thread issues" FALSE)
+ IF(ADD_ASAN)
+ SET(EXTRA_LIBS ${EXTRA_LIBS} asan )
+ ADD_DEFINITIONS(-fsanitize=address -fno-omit-frame-pointer)
+ ENDIF()
+ IF(ADD_TSAN)
+ SET(EXTRA_LIBS ${EXTRA_LIBS} tsan )
+ SET(USE_CLANG TRUE)
+ ADD_DEFINITIONS(-fsanitize=thread -fno-omit-frame-pointer -fPIC -g) #use with clang
+ ENDIF()
+ IF(ADD_TSAN AND ADD_ASAN)
+ message(FATAL_ERROR "TSAN and ASAN cannot be used at the same time")
+ ENDIF()
+ENDIF()
-set(CMAKE_CXX_COMPILER "/usr/bin/clang++-3.8" CACHE string "clang++ compiler" FORCE)
+project(libnitrokey)
+SET(PROJECT_VERSION "3.0-alpha")
+set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wno-gnu-variable-sized-type-not-at-end -g3" )
-SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lhidapi-libusb" )
+OPTION(LIBNITROKEY_STATIC "Build libnitrokey statically" TRUE)
-include_directories(include unittest/Catch/include)
+OPTION(COMPILE_TESTS "Compile tests" FALSE)
+IF (NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE RelWithDebInfo)
+ENDIF()
+MESSAGE("${PROJECT_NAME}: Build type: ${CMAKE_BUILD_TYPE}")
+include_directories(hidapi)
+include_directories(include)
set(SOURCE_FILES
include/command.h
include/command_id.h
@@ -22,19 +49,79 @@ set(SOURCE_FILES
include/NitrokeyManager.h
include/stick10_commands.h
include/stick20_commands.h
- NK_C_API.h
+ include/CommandFailedException.h
+ include/LibraryException.h
+ include/LongOperationInProgressException.h
+ include/stick10_commands_0.8.h
command_id.cc
device.cc
log.cc
misc.cc
NitrokeyManager.cc
- NK_C_API.cc include/CommandFailedException.h include/LibraryException.h
- unittest/test_C_API.cpp
- unittest/catch_main.cpp
- unittest/test2.cc
- unittest/test3.cc
- include/LongOperationInProgressException.h
- include/stick10_commands_0.8.h
- )
-
-add_executable(libnitrokey ${SOURCE_FILES}) \ No newline at end of file
+ NK_C_API.h
+ NK_C_API.cc
+ DeviceCommunicationExceptions.cpp)
+
+IF(UNIX)
+# add_library(hidapi-libusb STATIC hidapi/libusb/hid.c )
+ELSEIF(WIN32)
+ include_directories(hidapi/hidapi)
+ add_library(hidapi-libusb STATIC hidapi/windows/hid.c )
+ target_link_libraries(hidapi-libusb setupapi)
+ENDIF()
+
+IF (NOT LIBNITROKEY_STATIC)
+ add_library(nitrokey SHARED ${SOURCE_FILES})
+ add_library(nitrokey-log SHARED ${SOURCE_FILES})
+ install (TARGETS nitrokey DESTINATION "lib")
+ SET(LIBNAME nitrokey)
+ELSE()
+ add_library(nitrokey-static STATIC ${SOURCE_FILES})
+ add_library(nitrokey-static-log STATIC ${SOURCE_FILES})
+ SET(LIBNAME nitrokey-static)
+ENDIF()
+target_link_libraries(${LIBNAME} hidapi-libusb)
+target_link_libraries(${LIBNAME}-log hidapi-libusb)
+
+SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES COMPILE_DEFINITIONS "NO_LOG")
+
+
+file(GLOB LIB_INCLUDES "include/libnitrokey/*.h")
+install (FILES ${LIB_INCLUDES} DESTINATION "include")
+
+IF (COMPILE_TESTS)
+ include_directories(unittest/Catch/include)
+
+ add_library(catch SHARED unittest/catch_main.cpp )
+
+ add_executable (test_C_API unittest/test_C_API.cpp)
+ target_link_libraries (test_C_API ${EXTRA_LIBS} ${LIBNAME} catch)
+
+ add_executable (test2 unittest/test2.cc)
+ target_link_libraries (test2 ${EXTRA_LIBS} ${LIBNAME} catch)
+
+ add_executable (test3 unittest/test3.cc)
+ target_link_libraries (test3 ${EXTRA_LIBS} ${LIBNAME} catch)
+
+ add_executable (test_HOTP unittest/test_HOTP.cc)
+ target_link_libraries (test_HOTP ${EXTRA_LIBS} ${LIBNAME} catch)
+
+ add_executable (test1 unittest/test.cc)
+ target_link_libraries (test1 ${EXTRA_LIBS} ${LIBNAME} catch)
+
+ #run with 'make test' or 'ctest'
+ #needs connected PRO device for success
+ #warning: it may delete data on the device
+ include (CTest)
+ add_test (runs test_C_API)
+ENDIF()
+
+
+#SET(CPACK_GENERATOR
+# "DEB;RPM")
+# build a CPack driven installer package
+include (InstallRequiredSystemLibraries)
+set (CPACK_RESOURCE_FILE_LICENSE
+ "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+set (CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
+include (CPack) \ No newline at end of file
diff --git a/CMakeSettings.json b/CMakeSettings.json
new file mode 100644
index 0000000..e8c1f1d
--- /dev/null
+++ b/CMakeSettings.json
@@ -0,0 +1,37 @@
+{
+ // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
+ "configurations": [
+ {
+ "name": "x86-Debug",
+ "generator": "Visual Studio 15 2017",
+ "configurationType" : "Debug",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "",
+ "buildCommandArgs": "-m -v:minimal"
+ },
+ {
+ "name": "x86-Release",
+ "generator": "Visual Studio 15 2017",
+ "configurationType" : "Release",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "",
+ "buildCommandArgs": "-m -v:minimal"
+ },
+ {
+ "name": "x64-Debug",
+ "generator": "Visual Studio 15 2017 Win64",
+ "configurationType" : "Debug",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "",
+ "buildCommandArgs": "-m -v:minimal"
+ },
+ {
+ "name": "x64-Release",
+ "generator": "Visual Studio 15 2017 Win64",
+ "configurationType" : "Release",
+ "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}",
+ "cmakeCommandArgs": "",
+ "buildCommandArgs": "-m -v:minimal"
+ }
+ ]
+} \ No newline at end of file
diff --git a/DeviceCommunicationExceptions.cpp b/DeviceCommunicationExceptions.cpp
new file mode 100644
index 0000000..a470a48
--- /dev/null
+++ b/DeviceCommunicationExceptions.cpp
@@ -0,0 +1,3 @@
+#include "DeviceCommunicationExceptions.h"
+
+std::atomic_int DeviceCommunicationException::occurred {0};
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 977eae6..0000000
--- a/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-CC = $(PREFIX)-gcc
-#CXX = $(PREFIX)-g++
-CXX = clang++-3.8
-LD = $(CXX)
-
-INCLUDE = -Iinclude/
-LIB = -lhidapi-libusb
-BUILD = build
-
-CXXFLAGS = -std=c++14 -fPIC -Wno-gnu-variable-sized-type-not-at-end
-SOFLAGS = -shared
-
-CXXSOURCES = $(wildcard *.cc)
-OBJ = $(CXXSOURCES:%.cc=$(BUILD)/%.o)
-DEPENDS = $(CXXSOURCES:%.cc=$(BUILD)/%.d)
-
-all: $(OBJ) $(BUILD)/libnitrokey.so unittest
-
-lib: $(OBJ) $(BUILD)/libnitrokey.so
-
-$(BUILD)/libnitrokey.so: $(OBJ) $(DEPENDS)
- $(CXX) $(SOFLAGS) $(OBJ) $(LIB) -o $@
-
-$(BUILD)/%.d: %.cc
- $(CXX) -M $< -o $@ $(INCLUDE) $(CXXFLAGS)
-
-$(BUILD)/%.o: %.cc $(DEPENDS)
- $(CXX) -c $< -o $@ $(INCLUDE) $(CXXFLAGS)
-
-clean:
- rm -f $(OBJ)
- rm -f $(BUILD)/libnitrokey.so
- ${MAKE} -C unittest clean
-
-mrproper: clean
- rm -f $(BUILD)/*.d
- ${MAKE} -C unittest mrproper
-
-unittest: $(BUILD)/libnitrokey.so
- ${MAKE} -C unittest
- cd unittest/build && ln -fs ../../build/libnitrokey.so .
-
-.PHONY: all clean mrproper unittest
-
-include $(wildcard build/*.d)
diff --git a/NK_C_API.cc b/NK_C_API.cc
index e513a3b..262a0a4 100644
--- a/NK_C_API.cc
+++ b/NK_C_API.cc
@@ -1,6 +1,7 @@
#include <cstring>
#include "NK_C_API.h"
#include "include/LibraryException.h"
+#include "include/cxx_semantics.h"
using namespace nitrokey;
@@ -75,15 +76,16 @@ uint8_t get_without_result(T func){
return NK_last_command_status;
}
+
extern "C"
{
-extern uint8_t NK_get_last_command_status(){
+NK_C_API uint8_t NK_get_last_command_status(){
auto _copy = NK_last_command_status;
NK_last_command_status = 0;
return _copy;
}
-extern int NK_login(const char *device_model) {
+NK_C_API int NK_login(const char *device_model) {
auto m = NitrokeyManager::instance();
try {
NK_last_command_status = 0;
@@ -100,14 +102,14 @@ extern int NK_login(const char *device_model) {
return 0;
}
-extern int NK_logout() {
+NK_C_API int NK_logout() {
auto m = NitrokeyManager::instance();
return get_without_result( [&](){
m->disconnect();
});
}
-extern int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password){
+NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password){
auto m = NitrokeyManager::instance();
return get_without_result( [&](){
return m->first_authenticate(admin_password, admin_temporary_password);
@@ -115,34 +117,34 @@ extern int NK_first_authenticate(const char* admin_password, const char* admin_t
}
-extern int NK_user_authenticate(const char* user_password, const char* user_temporary_password){
+NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password){
auto m = NitrokeyManager::instance();
return get_without_result( [&](){
m->user_authenticate(user_password, user_temporary_password);
});
}
-extern int NK_factory_reset(const char* admin_password){
+NK_C_API int NK_factory_reset(const char* admin_password){
auto m = NitrokeyManager::instance();
return get_without_result( [&](){
m->factory_reset(admin_password);
});
}
-extern int NK_build_aes_key(const char* admin_password){
+NK_C_API int NK_build_aes_key(const char* admin_password){
auto m = NitrokeyManager::instance();
return get_without_result( [&](){
m->build_aes_key(admin_password);
});
}
-extern int NK_unlock_user_password(const char *admin_password, const char *new_user_password) {
+NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password) {
auto m = NitrokeyManager::instance();
return get_without_result( [&](){
m->unlock_user_password(admin_password, new_user_password);
});
}
-extern int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
+NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock, bool enable_user_password,
bool delete_user_password,
const char *admin_temporary_password) {
auto m = NitrokeyManager::instance();
@@ -152,7 +154,7 @@ extern int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock
}
-extern uint8_t* NK_read_config(){
+NK_C_API uint8_t* NK_read_config(){
auto m = NitrokeyManager::instance();
return get_with_array_result( [&](){
auto v = m->read_config();
@@ -165,17 +167,17 @@ void clear_string(std::string &s){
std::fill(s.begin(), s.end(), ' ');
}
-extern const char * NK_status() {
+NK_C_API const char * NK_status() {
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
- string && s = m->get_status();
+ string && s = m->get_status_as_string();
char * rs = strdup(s.c_str());
clear_string(s);
return rs;
});
}
-extern const char * NK_device_serial_number(){
+NK_C_API const char * NK_device_serial_number(){
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
string && s = m->get_serial_number();
@@ -185,23 +187,23 @@ extern const char * NK_device_serial_number(){
});
}
-extern uint32_t NK_get_hotp_code(uint8_t slot_number) {
+NK_C_API uint32_t NK_get_hotp_code(uint8_t slot_number) {
return NK_get_hotp_code_PIN(slot_number, "");
}
-extern uint32_t NK_get_hotp_code_PIN(uint8_t slot_number, const char* user_temporary_password){
+NK_C_API uint32_t NK_get_hotp_code_PIN(uint8_t slot_number, const char* user_temporary_password){
auto m = NitrokeyManager::instance();
return get_with_result([&](){
return m->get_HOTP_code(slot_number, user_temporary_password);
});
}
-extern uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
+NK_C_API uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
uint8_t last_interval){
return NK_get_totp_code_PIN(slot_number, challenge, last_totp_time, last_interval, "");
}
-extern uint32_t NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
+NK_C_API uint32_t NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
uint8_t last_interval, const char* user_temporary_password){
auto m = NitrokeyManager::instance();
return get_with_result([&](){
@@ -209,21 +211,21 @@ extern uint32_t NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge, ui
});
}
-extern int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) {
+NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password) {
auto m = NitrokeyManager::instance();
return get_without_result([&]{
m->erase_hotp_slot(slot_number, temporary_password);
});
}
-extern int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) {
+NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password) {
auto m = NitrokeyManager::instance();
return get_without_result([&]{
m->erase_totp_slot(slot_number, temporary_password);
});
}
-extern int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
+NK_C_API int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
const char *temporary_password) {
auto m = NitrokeyManager::instance();
@@ -233,7 +235,7 @@ extern int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const
});
}
-extern int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
+NK_C_API int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
const char *temporary_password) {
auto m = NitrokeyManager::instance();
@@ -243,14 +245,14 @@ extern int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const
});
}
-extern const char* NK_get_totp_slot_name(uint8_t slot_number){
+NK_C_API const char* NK_get_totp_slot_name(uint8_t slot_number){
auto m = NitrokeyManager::instance();
return get_with_string_result([&]() {
const auto slot_name = m->get_totp_slot_name(slot_number);
return slot_name;
});
}
-extern const char* NK_get_hotp_slot_name(uint8_t slot_number){
+NK_C_API const char* NK_get_hotp_slot_name(uint8_t slot_number){
auto m = NitrokeyManager::instance();
return get_with_string_result([&]() {
const auto slot_name = m->get_hotp_slot_name(slot_number);
@@ -258,46 +260,46 @@ extern const char* NK_get_hotp_slot_name(uint8_t slot_number){
});
}
-extern void NK_set_debug(bool state){
+NK_C_API void NK_set_debug(bool state){
auto m = NitrokeyManager::instance();
m->set_debug(state);
}
-extern int NK_totp_set_time(uint64_t time){
+NK_C_API int NK_totp_set_time(uint64_t time){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->set_time(time);
});
}
-extern int NK_totp_get_time(){
+NK_C_API int NK_totp_get_time(){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
- m->get_time(); // FIXME check how that should work
+ m->get_time(0); // FIXME check how that should work
});
}
-extern int NK_change_admin_PIN(char *current_PIN, char *new_PIN){
+NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->change_admin_PIN(current_PIN, new_PIN);
});
}
-extern int NK_change_user_PIN(char *current_PIN, char *new_PIN){
+NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->change_user_PIN(current_PIN, new_PIN);
});
}
-extern int NK_enable_password_safe(const char *user_pin){
+NK_C_API int NK_enable_password_safe(const char *user_pin){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->enable_password_safe(user_pin);
});
}
-extern uint8_t * NK_get_password_safe_slot_status(){
+NK_C_API uint8_t * NK_get_password_safe_slot_status(){
auto m = NitrokeyManager::instance();
return get_with_array_result( [&](){
auto slot_status = m->get_password_safe_slot_status();
@@ -306,47 +308,47 @@ extern uint8_t * NK_get_password_safe_slot_status(){
}
-extern uint8_t NK_get_user_retry_count(){
+NK_C_API uint8_t NK_get_user_retry_count(){
auto m = NitrokeyManager::instance();
return get_with_result([&](){
return m->get_user_retry_count();
});
}
-extern uint8_t NK_get_admin_retry_count(){
+NK_C_API uint8_t NK_get_admin_retry_count(){
auto m = NitrokeyManager::instance();
return get_with_result([&](){
return m->get_admin_retry_count();
});
}
-extern int NK_lock_device(){
+NK_C_API int NK_lock_device(){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->lock_device();
});
}
-extern const char *NK_get_password_safe_slot_name(uint8_t slot_number) {
+NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number) {
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
return m->get_password_safe_slot_name(slot_number);
});
}
-extern const char *NK_get_password_safe_slot_login(uint8_t slot_number) {
+NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number) {
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
return m->get_password_safe_slot_login(slot_number);
});
}
-extern const char *NK_get_password_safe_slot_password(uint8_t slot_number) {
+NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number) {
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
return m->get_password_safe_slot_password(slot_number);
});
}
-extern int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
+NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
const char *slot_password) {
auto m = NitrokeyManager::instance();
return get_without_result([&](){
@@ -354,21 +356,21 @@ extern int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_nam
});
}
-extern int NK_erase_password_safe_slot(uint8_t slot_number) {
+NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number) {
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->erase_password_safe_slot(slot_number);
});
}
-extern int NK_is_AES_supported(const char *user_password) {
+NK_C_API int NK_is_AES_supported(const char *user_password) {
auto m = NitrokeyManager::instance();
return get_with_result([&](){
return (uint8_t) m->is_AES_supported(user_password);
});
}
-extern int NK_login_auto() {
+NK_C_API int NK_login_auto() {
auto m = NitrokeyManager::instance();
return get_with_result([&](){
return (uint8_t) m->connect();
@@ -377,28 +379,42 @@ extern int NK_login_auto() {
// storage commands
-extern int NK_send_startup(uint64_t seconds_from_epoch){
+NK_C_API int NK_send_startup(uint64_t seconds_from_epoch){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->send_startup(seconds_from_epoch);
});
}
-extern int NK_unlock_encrypted_volume(const char* user_pin){
+NK_C_API int NK_unlock_encrypted_volume(const char* user_pin){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->unlock_encrypted_volume(user_pin);
});
}
-extern int NK_unlock_hidden_volume(const char* hidden_volume_password){
+NK_C_API int NK_lock_encrypted_volume(){
+ auto m = NitrokeyManager::instance();
+ return get_without_result([&](){
+ m->lock_encrypted_volume();
+ });
+}
+
+NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->unlock_hidden_volume(hidden_volume_password);
});
}
-extern int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
+NK_C_API int NK_lock_hidden_volume(){
+ auto m = NitrokeyManager::instance();
+ return get_without_result([&](){
+ m->lock_hidden_volume();
+ });
+}
+
+NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
const char *hidden_volume_password){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
@@ -407,42 +423,42 @@ extern int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8
});
}
-extern int NK_set_unencrypted_read_only(const char* user_pin){
+NK_C_API int NK_set_unencrypted_read_only(const char* user_pin){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->set_unencrypted_read_only(user_pin);
});
}
-extern int NK_set_unencrypted_read_write(const char* user_pin){
+NK_C_API int NK_set_unencrypted_read_write(const char* user_pin){
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->set_unencrypted_read_write(user_pin);
});
}
-extern int NK_export_firmware(const char* admin_pin) {
+NK_C_API int NK_export_firmware(const char* admin_pin) {
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->export_firmware(admin_pin) ;
});
}
-extern int NK_clear_new_sd_card_warning(const char* admin_pin) {
+NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) {
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->clear_new_sd_card_warning(admin_pin);
});
}
-extern int NK_fill_SD_card_with_random_data(const char* admin_pin) {
+NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) {
auto m = NitrokeyManager::instance();
return get_without_result([&](){
m->fill_SD_card_with_random_data(admin_pin);
});
}
-extern int NK_change_update_password(const char* current_update_password,
+NK_C_API int NK_change_update_password(const char* current_update_password,
const char* new_update_password) {
auto m = NitrokeyManager::instance();
return get_without_result([&](){
@@ -450,31 +466,31 @@ extern int NK_change_update_password(const char* current_update_password,
});
}
-extern const char* NK_get_status_storage_as_string() {
+NK_C_API const char* NK_get_status_storage_as_string() {
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
return m->get_status_storage_as_string();
});
}
-extern const char* NK_get_SD_usage_data_as_string() {
+NK_C_API const char* NK_get_SD_usage_data_as_string() {
auto m = NitrokeyManager::instance();
return get_with_string_result([&](){
return m->get_SD_usage_data_as_string();
});
}
-extern int NK_get_progress_bar_value() {
+NK_C_API int NK_get_progress_bar_value() {
auto m = NitrokeyManager::instance();
return get_with_result([&](){
return m->get_progress_bar_value();
});
}
-extern int NK_get_major_firmware_version(){
+NK_C_API int NK_get_major_firmware_version(){
auto m = NitrokeyManager::instance();
return get_with_result([&](){
- return m->get_major_firmware_version();
+ return m->get_minor_firmware_version();
});
}
diff --git a/NK_C_API.h b/NK_C_API.h
index 7f01900..f52034a 100644
--- a/NK_C_API.h
+++ b/NK_C_API.h
@@ -6,57 +6,59 @@
#include "include/NitrokeyManager.h"
#include "include/inttypes.h"
+#define NK_C_API
+
extern "C"
{
/**
* Set debug level of messages written on stderr
* @param state state=True - all messages, state=False - only errors level
*/
-extern void NK_set_debug(bool state);
+NK_C_API void NK_set_debug(bool state);
/**
* Connect to device of given model. Currently library can be connected only to one device at once.
* @param device_model char 'S': Nitrokey Storage, 'P': Nitrokey Pro
* @return 1 if connected, 0 if wrong model or cannot connect
*/
-extern int NK_login(const char *device_model);
+NK_C_API int NK_login(const char *device_model);
/**
* Connect to first available device, starting checking from Pro 1st to Storage 2nd.
* @return 1 if connected, 0 if wrong model or cannot connect
*/
-extern int NK_login_auto();
+NK_C_API int NK_login_auto();
/**
* Disconnect from the device.
* @return command processing error code
*/
-extern int NK_logout();
+NK_C_API int NK_logout();
/**
* Return the debug status string. Debug purposes.
* @return command processing error code
*/
-extern const char * NK_status();
+NK_C_API const char * NK_status();
/**
* Return the device's serial number string in hex.
* @return string device's serial number in hex
*/
-extern const char * NK_device_serial_number();
+NK_C_API const char * NK_device_serial_number();
/**
* Get last command processing status. Useful for commands which returns the results of their own and could not return
* an error code.
* @return previous command processing error code
*/
-extern uint8_t NK_get_last_command_status();
+NK_C_API uint8_t NK_get_last_command_status();
/**
* Lock device - cancel any user device unlocking.
* @return command processing error code
*/
-extern int NK_lock_device();
+NK_C_API int NK_lock_device();
/**
* Authenticates the user on USER privilages with user_password and sets user's temporary password on device to user_temporary_password.
@@ -64,7 +66,7 @@ extern int NK_lock_device();
* @param user_temporary_password char[25](Pro) user temporary password to be set on device for further communication (authentication command)
* @return command processing error code
*/
-extern int NK_user_authenticate(const char* user_password, const char* user_temporary_password);
+NK_C_API int NK_user_authenticate(const char* user_password, const char* user_temporary_password);
/**
* Authenticates the user on ADMIN privilages with admin_password and sets user's temporary password on device to admin_temporary_password.
@@ -72,28 +74,28 @@ extern int NK_user_authenticate(const char* user_password, const char* user_temp
* @param admin_temporary_password char[25](Pro) admin temporary password to be set on device for further communication (authentication command)
* @return command processing error code
*/
-extern int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password);
+NK_C_API int NK_first_authenticate(const char* admin_password, const char* admin_temporary_password);
/**
* Execute a factory reset.
* @param admin_password char[20](Pro) current administrator PIN
* @return command processing error code
*/
-extern int NK_factory_reset(const char* admin_password);
+NK_C_API int NK_factory_reset(const char* admin_password);
/**
* Generates AES key on the device
* @param admin_password char[20](Pro) current administrator PIN
* @return command processing error code
*/
-extern int NK_build_aes_key(const char* admin_password);
+NK_C_API int NK_build_aes_key(const char* admin_password);
/**
* Unlock user PIN locked after 3 incorrect codes tries.
* @param admin_password char[20](Pro) current administrator PIN
* @return command processing error code
*/
-extern int NK_unlock_user_password(const char *admin_password, const char *new_user_password);
+NK_C_API int NK_unlock_user_password(const char *admin_password, const char *new_user_password);
/**
* Write general config to the device
@@ -106,7 +108,7 @@ extern int NK_unlock_user_password(const char *admin_password, const char *new_u
* @param admin_temporary_password current admin temporary password
* @return command processing error code
*/
-extern int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock,
+NK_C_API int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock,
bool enable_user_password, bool delete_user_password, const char *admin_temporary_password);
/**
@@ -120,7 +122,7 @@ extern int NK_write_config(uint8_t numlock, uint8_t capslock, uint8_t scrolllock
uint8_t delete_user_password;
*/
-extern uint8_t* NK_read_config();
+NK_C_API uint8_t* NK_read_config();
//OTP
@@ -129,14 +131,14 @@ extern uint8_t* NK_read_config();
* @param slot_number TOTP slot number, slot_number<15
* @return char[20](Pro) the name of the slot
*/
-extern const char * NK_get_totp_slot_name(uint8_t slot_number);
+NK_C_API const char * NK_get_totp_slot_name(uint8_t slot_number);
/**
*
* @param slot_number HOTP slot number, slot_number<3
* @return char[20](Pro) the name of the slot
*/
-extern const char * NK_get_hotp_slot_name(uint8_t slot_number);
+NK_C_API const char * NK_get_hotp_slot_name(uint8_t slot_number);
/**
* Erase HOTP slot data from the device
@@ -144,7 +146,7 @@ extern const char * NK_get_hotp_slot_name(uint8_t slot_number);
* @param temporary_password admin temporary password
* @return command processing error code
*/
-extern int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password);
+NK_C_API int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_password);
/**
* Erase TOTP slot data from the device
@@ -152,7 +154,7 @@ extern int NK_erase_hotp_slot(uint8_t slot_number, const char *temporary_passwor
* @param temporary_password admin temporary password
* @return command processing error code
*/
-extern int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password);
+NK_C_API int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_password);
/**
* Write HOTP slot data to the device
@@ -167,7 +169,7 @@ extern int NK_erase_totp_slot(uint8_t slot_number, const char *temporary_passwor
* @param temporary_password char[25](Pro) admin temporary password
* @return command processing error code
*/
-extern int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
+NK_C_API int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
const char *temporary_password);
@@ -184,7 +186,7 @@ extern int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const
* @param temporary_password char[20](Pro) admin temporary password
* @return command processing error code
*/
-extern int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
+NK_C_API int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
const char *temporary_password);
@@ -193,7 +195,7 @@ extern int NK_write_totp_slot(uint8_t slot_number, const char *slot_name, const
* @param slot_number HOTP slot number, slot_number<3
* @return HOTP code
*/
-extern uint32_t NK_get_hotp_code(uint8_t slot_number);
+NK_C_API uint32_t NK_get_hotp_code(uint8_t slot_number);
/**
* Get HOTP code from the device (PIN protected)
@@ -202,7 +204,7 @@ extern uint32_t NK_get_hotp_code(uint8_t slot_number);
* otherwise should be set to empty string - ''
* @return HOTP code
*/
-extern uint32_t NK_get_hotp_code_PIN(uint8_t slot_number, const char* user_temporary_password);
+NK_C_API uint32_t NK_get_hotp_code_PIN(uint8_t slot_number, const char* user_temporary_password);
/**
* Get TOTP code from the device
@@ -212,7 +214,7 @@ extern uint32_t NK_get_hotp_code_PIN(uint8_t slot_number, const char* user_tempo
* @param last_interval last interval
* @return TOTP code
*/
-extern uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, uint8_t last_interval);
+NK_C_API uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, uint8_t last_interval);
/**
* Get TOTP code from the device (PIN protected)
@@ -224,7 +226,7 @@ extern uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64
* otherwise should be set to empty string - ''
* @return TOTP code
*/
-extern uint32_t NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge,
+NK_C_API uint32_t NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge,
uint64_t last_totp_time, uint8_t last_interval, const char* user_temporary_password);
/**
@@ -232,9 +234,9 @@ extern uint32_t NK_get_totp_code_PIN(uint8_t slot_number, uint64_t challenge,
* @param time seconds in unix epoch (from 01.01.1970)
* @return command processing error code
*/
-extern int NK_totp_set_time(uint64_t time);
+NK_C_API int NK_totp_set_time(uint64_t time);
-extern int NK_totp_get_time();
+NK_C_API int NK_totp_get_time();
//passwords
/**
* Change administrator PIN
@@ -242,7 +244,7 @@ extern int NK_totp_get_time();
* @param new_PIN char[25](Pro) new PIN
* @return command processing error code
*/
-extern int NK_change_admin_PIN(char *current_PIN, char *new_PIN);
+NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN);
/**
* Change user PIN
@@ -250,20 +252,20 @@ extern int NK_change_admin_PIN(char *current_PIN, char *new_PIN);
* @param new_PIN char[25](Pro) new PIN
* @return command processing error code
*/
-extern int NK_change_user_PIN(char *current_PIN, char *new_PIN);
+NK_C_API int NK_change_user_PIN(const char *current_PIN, const char *new_PIN);
/**
* Get retry count of user PIN
* @return user PIN retry count
*/
-extern uint8_t NK_get_user_retry_count();
+NK_C_API uint8_t NK_get_user_retry_count();
/**
* Get retry count of admin PIN
* @return admin PIN retry count
*/
-extern uint8_t NK_get_admin_retry_count();
+NK_C_API uint8_t NK_get_admin_retry_count();
//password safe
/**
@@ -271,34 +273,34 @@ extern uint8_t NK_get_admin_retry_count();
* @param user_pin char[30](Pro) current user PIN
* @return command processing error code
*/
-extern int NK_enable_password_safe(const char *user_pin);
+NK_C_API int NK_enable_password_safe(const char *user_pin);
/**
* Get password safe slots' status
* @return uint8_t[16] slot statuses - each byte represents one slot with 0 (not programmed) and 1 (programmed)
*/
-extern uint8_t * NK_get_password_safe_slot_status();
+NK_C_API uint8_t * NK_get_password_safe_slot_status();
/**
* Get password safe slot name
* @param slot_number password safe slot number, slot_number<16
* @return slot name
*/
-extern const char *NK_get_password_safe_slot_name(uint8_t slot_number);
+NK_C_API const char *NK_get_password_safe_slot_name(uint8_t slot_number);
/**
* Get password safe slot login
* @param slot_number password safe slot number, slot_number<16
* @return login from the PWS slot
*/
-extern const char *NK_get_password_safe_slot_login(uint8_t slot_number);
+NK_C_API const char *NK_get_password_safe_slot_login(uint8_t slot_number);
/**
* Get the password safe slot password
* @param slot_number password safe slot number, slot_number<16
* @return password from the PWS slot
*/
-extern const char *NK_get_password_safe_slot_password(uint8_t slot_number);
+NK_C_API const char *NK_get_password_safe_slot_password(uint8_t slot_number);
/**
* Write password safe data to the slot
@@ -308,7 +310,7 @@ extern const char *NK_get_password_safe_slot_password(uint8_t slot_number);
* @param slot_password char[20](Pro) password string
* @return command processing error code
*/
-extern int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name,
+NK_C_API int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_name,
const char *slot_login, const char *slot_password);
/**
@@ -316,19 +318,19 @@ extern int NK_write_password_safe_slot(uint8_t slot_number, const char *slot_nam
* @param slot_number password safe slot number, slot_number<16
* @return command processing error code
*/
-extern int NK_erase_password_safe_slot(uint8_t slot_number);
+NK_C_API int NK_erase_password_safe_slot(uint8_t slot_number);
/**
* Check whether AES is supported by the device
* @return 0 for no and 1 for yes
*/
-extern int NK_is_AES_supported(const char *user_password);
+NK_C_API int NK_is_AES_supported(const char *user_password);
/**
* Get device's major firmware version
* @return 7,8 for Pro and major for Storage
*/
-extern int NK_get_major_firmware_version();
+NK_C_API int NK_get_major_firmware_version();
@@ -340,7 +342,7 @@ extern int NK_get_major_firmware_version();
* Storage only
* @param seconds_from_epoch date and time expressed in seconds
*/
-extern int NK_send_startup(uint64_t seconds_from_epoch);
+NK_C_API int NK_send_startup(uint64_t seconds_from_epoch);
/**
* Unlock encrypted volume.
@@ -348,7 +350,13 @@ extern int NK_send_startup(uint64_t seconds_from_epoch);
* @param user_pin user pin 20 characters
* @return command processing error code
*/
-extern int NK_unlock_encrypted_volume(const char* user_pin);
+NK_C_API int NK_unlock_encrypted_volume(const char* user_pin);
+
+/**
+ * Locks encrypted volume
+ * @return command processing error code
+ */
+NK_C_API int NK_lock_encrypted_volume();
/**
* Unlock hidden volume and lock encrypted volume.
@@ -357,7 +365,13 @@ extern int NK_unlock_encrypted_volume(const char* user_pin);
* @param hidden_volume_password 20 characters
* @return command processing error code
*/
-extern int NK_unlock_hidden_volume(const char* hidden_volume_password);
+NK_C_API int NK_unlock_hidden_volume(const char* hidden_volume_password);
+
+/**
+ * Locks hidden volume
+ * @return command processing error code
+ */
+NK_C_API int NK_lock_hidden_volume();
/**
* Create hidden volume.
@@ -369,7 +383,7 @@ extern int NK_unlock_hidden_volume(const char* hidden_volume_password);
* @param hidden_volume_password 20 characters
* @return command processing error code
*/
-extern int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
+NK_C_API int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
const char *hidden_volume_password);
/**
@@ -380,7 +394,7 @@ extern int NK_create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8
* @param user_pin 20 characters
* @return command processing error code
*/
-extern int NK_set_unencrypted_read_only(const char* user_pin);
+NK_C_API int NK_set_unencrypted_read_only(const char* user_pin);
/**
* Make unencrypted volume read-write.
@@ -390,7 +404,7 @@ extern int NK_set_unencrypted_read_only(const char* user_pin);
* @param user_pin 20 characters
* @return command processing error code
*/
-extern int NK_set_unencrypted_read_write(const char* user_pin);
+NK_C_API int NK_set_unencrypted_read_write(const char* user_pin);
/**
* Exports device's firmware to unencrypted volume.
@@ -398,7 +412,7 @@ extern int NK_set_unencrypted_read_write(const char* user_pin);
* @param admin_pin 20 characters
* @return command processing error code
*/
-extern int NK_export_firmware(const char* admin_pin) ;
+NK_C_API int NK_export_firmware(const char* admin_pin) ;
/**
* Clear new SD card notification. It is set after factory reset.
@@ -406,7 +420,7 @@ extern int NK_export_firmware(const char* admin_pin) ;
* @param admin_pin 20 characters
* @return command processing error code
*/
-extern int NK_clear_new_sd_card_warning(const char* admin_pin) ;
+NK_C_API int NK_clear_new_sd_card_warning(const char* admin_pin) ;
/**
* Fill SD card with random data.
@@ -415,7 +429,7 @@ extern int NK_clear_new_sd_card_warning(const char* admin_pin) ;
* @param admin_pin 20 characters
* @return command processing error code
*/
-extern int NK_fill_SD_card_with_random_data(const char* admin_pin) ;
+NK_C_API int NK_fill_SD_card_with_random_data(const char* admin_pin) ;
/**
* Change update password.
@@ -426,7 +440,7 @@ extern int NK_fill_SD_card_with_random_data(const char* admin_pin) ;
* @param new_update_password 20 characters
* @return command processing error code
*/
-extern int NK_change_update_password(const char* current_update_password,
+NK_C_API int NK_change_update_password(const char* current_update_password,
const char* new_update_password);
/**
@@ -434,7 +448,7 @@ extern int NK_change_update_password(const char* current_update_password,
* Storage only
* @return string with devices attributes
*/
-extern const char* NK_get_status_storage_as_string();
+NK_C_API const char* NK_get_status_storage_as_string();
/**
* Get SD card usage attributes as string.
@@ -442,14 +456,14 @@ extern const char* NK_get_status_storage_as_string();
* Storage only
* @return string with SD card usage attributes
*/
-extern const char* NK_get_SD_usage_data_as_string();
+NK_C_API const char* NK_get_SD_usage_data_as_string();
/**
* Get progress value of current long operation.
* Storage only
* @return int in range 0-100 or -1 if device is not busy
*/
-extern int NK_get_progress_bar_value();
+NK_C_API int NK_get_progress_bar_value();
}
diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc
index ddec600..140d4d3 100644
--- a/NitrokeyManager.cc
+++ b/NitrokeyManager.cc
@@ -6,9 +6,15 @@
#include <unordered_map>
#include <stick20_commands.h>
#include "include/misc.h"
+#include <mutex>
+#include "include/cxx_semantics.h"
+
namespace nitrokey{
+ std::mutex mex_dev_com;
+
+
template <typename T>
void strcpyT(T& dest, const char* src){
@@ -16,7 +22,7 @@ namespace nitrokey{
// throw EmptySourceStringException(slot_number);
return;
const size_t s_dest = sizeof dest;
- nitrokey::log::Log::instance()(std::string("strcpyT sizes dest src ")
+ LOG(std::string("strcpyT sizes dest src ")
+std::to_string(s_dest)+ " "
+std::to_string(strlen(src))+ " "
,nitrokey::log::Loglevel::DEBUG_L2);
@@ -39,24 +45,25 @@ namespace nitrokey{
template <typename S, typename A, typename T>
void NitrokeyManager::authorize_packet(T &package, const char *admin_temporary_password, shared_ptr<Device> device){
if (!is_authorization_command_supported()){
- Log::instance()("Authorization command not supported, skipping", Loglevel::WARNING);
+ LOG("Authorization command not supported, skipping", Loglevel::WARNING);
}
auto auth = get_payload<A>();
strcpyT(auth.temporary_password, admin_temporary_password);
auth.crc_to_authorize = S::CommandTransaction::getCRC(package);
- A::CommandTransaction::run(*device, auth);
+ A::CommandTransaction::run(device, auth);
}
shared_ptr <NitrokeyManager> NitrokeyManager::_instance = nullptr;
- NitrokeyManager::NitrokeyManager() {
+ NitrokeyManager::NitrokeyManager() : device(nullptr)
+ {
set_debug(true);
}
NitrokeyManager::~NitrokeyManager() {
}
bool NitrokeyManager::connect() {
- this->disconnect();
+ std::lock_guard<std::mutex> lock(mex_dev_com);
vector< shared_ptr<Device> > devices = { make_shared<Stick10>(), make_shared<Stick20>() };
for( auto & d : devices ){
if (d->connect()){
@@ -68,7 +75,8 @@ namespace nitrokey{
bool NitrokeyManager::connect(const char *device_model) {
- this->disconnect();
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ LOG(__FUNCTION__, nitrokey::log::Loglevel::DEBUG_L2);
switch (device_model[0]){
case 'P':
device = make_shared<Stick10>();
@@ -83,20 +91,53 @@ namespace nitrokey{
}
shared_ptr<NitrokeyManager> NitrokeyManager::instance() {
+ static std::mutex mutex;
+ std::lock_guard<std::mutex> lock(mutex);
if (_instance == nullptr){
- _instance = shared_ptr<NitrokeyManager>(new NitrokeyManager());
+ _instance = make_shared<NitrokeyManager>();
}
return _instance;
}
+
+
bool NitrokeyManager::disconnect() {
- if (device == nullptr){
- return false;
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ return _disconnect_no_lock();
+ }
+
+ bool NitrokeyManager::_disconnect_no_lock() {
+ //do not use directly without locked mutex,
+ //used by could_be_enumerated, disconnect
+ if (device == nullptr){
+ return false;
+ }
+ const auto res = device->disconnect();
+ device = nullptr;
+ return res;
+ }
+
+ bool NitrokeyManager::is_connected() throw(){
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ if(device != nullptr){
+ auto connected = device->could_be_enumerated();
+ if(connected){
+ return true;
+ } else {
+ _disconnect_no_lock();
+ return false;
+ }
}
- const auto res = device->disconnect();
- device = nullptr;
- return res;
+ return false;
+ }
+
+ bool NitrokeyManager::could_current_device_be_enumerated() {
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ if (device != nullptr) {
+ return device->could_be_enumerated();
}
+ return false;
+ }
void NitrokeyManager::set_debug(bool state) {
if (state){
@@ -106,13 +147,37 @@ namespace nitrokey{
}
}
+
string NitrokeyManager::get_serial_number() {
- auto response = GetStatus::CommandTransaction::run(*device);
- return response.data().get_card_serial_hex();
+ switch (device->get_device_model()) {
+ case DeviceModel::PRO: {
+ auto response = GetStatus::CommandTransaction::run(device);
+ return nitrokey::misc::toHex(response.data().card_serial_u32);
+ }
+ break;
+
+ case DeviceModel::STORAGE:
+ {
+ auto response = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return nitrokey::misc::toHex(response.data().ActiveSmartCardID_u32);
+ }
+ break;
+ }
+ }
+
+ stick10::GetStatus::ResponsePayload NitrokeyManager::get_status(){
+ try{
+ auto response = GetStatus::CommandTransaction::run(device);
+ return response.data();
+ }
+ catch (DeviceSendingFailure &e){
+ disconnect();
+ throw;
+ }
}
- string NitrokeyManager::get_status() {
- auto response = GetStatus::CommandTransaction::run(*device);
+ string NitrokeyManager::get_status_as_string() {
+ auto response = GetStatus::CommandTransaction::run(device);
return response.data().dissect();
}
@@ -125,7 +190,7 @@ namespace nitrokey{
if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen
authorize_packet<GetHOTP, UserAuthorize>(gh, user_temporary_password, device);
}
- auto resp = GetHOTP::CommandTransaction::run(*device, gh);
+ auto resp = GetHOTP::CommandTransaction::run(device, gh);
return resp.data().code;
} else {
auto gh = get_payload<stick10_08::GetHOTP>();
@@ -133,7 +198,7 @@ namespace nitrokey{
if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0) {
strcpyT(gh.temporary_user_password, user_temporary_password);
}
- auto resp = stick10_08::GetHOTP::CommandTransaction::run(*device, gh);
+ auto resp = stick10_08::GetHOTP::CommandTransaction::run(device, gh);
return resp.data().code;
}
}
@@ -160,13 +225,13 @@ namespace nitrokey{
if(user_temporary_password != nullptr && strlen(user_temporary_password)!=0){ //FIXME use string instead of strlen
authorize_packet<GetTOTP, UserAuthorize>(gt, user_temporary_password, device);
}
- auto resp = GetTOTP::CommandTransaction::run(*device, gt);
+ auto resp = GetTOTP::CommandTransaction::run(device, gt);
return resp.data().code;
} else {
auto gt = get_payload<stick10_08::GetTOTP>();
strcpyT(gt.temporary_user_password, user_temporary_password);
gt.slot_number = slot_number;
- auto resp = stick10_08::GetTOTP::CommandTransaction::run(*device, gt);
+ auto resp = stick10_08::GetTOTP::CommandTransaction::run(device, gt);
return resp.data().code;
}
@@ -177,12 +242,12 @@ namespace nitrokey{
auto p = get_payload<EraseSlot>();
p.slot_number = slot_number;
authorize_packet<EraseSlot, Authorize>(p, temporary_password, device);
- auto resp = EraseSlot::CommandTransaction::run(*device,p);
+ auto resp = EraseSlot::CommandTransaction::run(device,p);
} else {
auto p = get_payload<stick10_08::EraseSlot>();
p.slot_number = slot_number;
strcpyT(p.temporary_admin_password, temporary_password);
- auto resp = stick10_08::EraseSlot::CommandTransaction::run(*device,p);
+ auto resp = stick10_08::EraseSlot::CommandTransaction::run(device,p);
}
return true;
}
@@ -255,7 +320,7 @@ namespace nitrokey{
break;
}
default:
- Log::instance()(string(__FILE__) + to_string(__LINE__) +
+ LOG(string(__FILE__) + to_string(__LINE__) +
string(__FUNCTION__) + string(" Unhandled device model for HOTP")
, Loglevel::DEBUG);
break;
@@ -266,7 +331,7 @@ namespace nitrokey{
authorize_packet<WriteToHOTPSlot, Authorize>(payload, temporary_password, device);
- auto resp = WriteToHOTPSlot::CommandTransaction::run(*device, payload);
+ auto resp = WriteToHOTPSlot::CommandTransaction::run(device, payload);
}
bool NitrokeyManager::write_TOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint16_t time_window,
@@ -296,7 +361,7 @@ namespace nitrokey{
strcpyT(payload2.temporary_admin_password, temporary_password);
strcpyT(payload2.data, slot_name);
payload2.setTypeName();
- stick10_08::SendOTPData::CommandTransaction::run(*device, payload2);
+ stick10_08::SendOTPData::CommandTransaction::run(device, payload2);
payload2.setTypeSecret();
payload2.id = 0;
@@ -312,7 +377,7 @@ namespace nitrokey{
const auto start = secret_bin.size() - remaining_secret_length;
memset(payload2.data, 0, sizeof(payload2.data));
vector_copy_ranged(payload2.data, secret_bin, start, bytesToCopy);
- stick10_08::SendOTPData::CommandTransaction::run(*device, payload2);
+ stick10_08::SendOTPData::CommandTransaction::run(device, payload2);
remaining_secret_length -= bytesToCopy;
payload2.id++;
}
@@ -325,7 +390,7 @@ namespace nitrokey{
payload.use_tokenID = use_tokenID;
payload.slot_counter_or_interval = counter_or_interval;
payload.slot_number = internal_slot_number;
- stick10_08::WriteToOTPSlot::CommandTransaction::run(*device, payload);
+ stick10_08::WriteToOTPSlot::CommandTransaction::run(device, payload);
}
void NitrokeyManager::write_TOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret,
@@ -344,7 +409,7 @@ namespace nitrokey{
authorize_packet<WriteToTOTPSlot, Authorize>(payload, temporary_password, device);
- auto resp = WriteToTOTPSlot::CommandTransaction::run(*device, payload);
+ auto resp = WriteToTOTPSlot::CommandTransaction::run(device, payload);
}
const char * NitrokeyManager::get_totp_slot_name(uint8_t slot_number) {
@@ -361,7 +426,7 @@ namespace nitrokey{
const char * NitrokeyManager::get_slot_name(uint8_t slot_number) {
auto payload = get_payload<GetSlotName>();
payload.slot_number = slot_number;
- auto resp = GetSlotName::CommandTransaction::run(*device, payload);
+ auto resp = GetSlotName::CommandTransaction::run(device, payload);
return strdup((const char *) resp.data().slot_name);
}
@@ -369,7 +434,7 @@ namespace nitrokey{
auto authreq = get_payload<FirstAuthenticate>();
strcpyT(authreq.card_password, pin);
strcpyT(authreq.temporary_password, temporary_password);
- FirstAuthenticate::CommandTransaction::run(*device, authreq);
+ FirstAuthenticate::CommandTransaction::run(device, authreq);
return true;
}
@@ -377,34 +442,35 @@ namespace nitrokey{
auto p = get_payload<SetTime>();
p.reset = 1;
p.time = time;
- SetTime::CommandTransaction::run(*device, p);
+ SetTime::CommandTransaction::run(device, p);
return false;
}
- bool NitrokeyManager::get_time() {
+ bool NitrokeyManager::get_time(uint64_t time) {
auto p = get_payload<SetTime>();
p.reset = 0;
- SetTime::CommandTransaction::run(*device, p);
- return false;
+ p.time = time;
+ SetTime::CommandTransaction::run(device, p);
+ return true;
}
- void NitrokeyManager::change_user_PIN(char *current_PIN, char *new_PIN) {
+ void NitrokeyManager::change_user_PIN(const char *current_PIN, const char *new_PIN) {
change_PIN_general<ChangeUserPin, PasswordKind::User>(current_PIN, new_PIN);
}
- void NitrokeyManager::change_admin_PIN(char *current_PIN, char *new_PIN) {
+ void NitrokeyManager::change_admin_PIN(const char *current_PIN, const char *new_PIN) {
change_PIN_general<ChangeAdminPin, PasswordKind::Admin>(current_PIN, new_PIN);
}
template <typename ProCommand, PasswordKind StoKind>
- void NitrokeyManager::change_PIN_general(char *current_PIN, char *new_PIN) {
+ void NitrokeyManager::change_PIN_general(const char *current_PIN, const char *new_PIN) {
switch (device->get_device_model()){
case DeviceModel::PRO:
{
auto p = get_payload<ProCommand>();
strcpyT(p.old_pin, current_PIN);
strcpyT(p.new_pin, new_PIN);
- ProCommand::CommandTransaction::run(*device, p);
+ ProCommand::CommandTransaction::run(device, p);
}
break;
//in Storage change admin/user pin is divided to two commands with 20 chars field len
@@ -416,8 +482,8 @@ namespace nitrokey{
auto p2 = get_payload<ChangeAdminUserPin20New>();
strcpyT(p2.password, new_PIN);
p2.set_kind(StoKind);
- ChangeAdminUserPin20Current::CommandTransaction::run(*device, p);
- ChangeAdminUserPin20New::CommandTransaction::run(*device, p2);
+ ChangeAdminUserPin20Current::CommandTransaction::run(device, p);
+ ChangeAdminUserPin20New::CommandTransaction::run(device, p2);
}
break;
}
@@ -428,15 +494,15 @@ namespace nitrokey{
//The following command will cancel enabling PWS if it is not supported
auto a = get_payload<IsAESSupported>();
strcpyT(a.user_password, user_pin);
- IsAESSupported::CommandTransaction::run(*device, a);
+ IsAESSupported::CommandTransaction::run(device, a);
auto p = get_payload<EnablePasswordSafe>();
strcpyT(p.user_password, user_pin);
- EnablePasswordSafe::CommandTransaction::run(*device, p);
+ EnablePasswordSafe::CommandTransaction::run(device, p);
}
vector <uint8_t> NitrokeyManager::get_password_safe_slot_status() {
- auto responsePayload = GetPasswordSafeSlotStatus::CommandTransaction::run(*device);
+ auto responsePayload = GetPasswordSafeSlotStatus::CommandTransaction::run(device);
vector<uint8_t> v = vector<uint8_t>(responsePayload.data().password_safe_status,
responsePayload.data().password_safe_status
+ sizeof(responsePayload.data().password_safe_status));
@@ -445,28 +511,29 @@ namespace nitrokey{
uint8_t NitrokeyManager::get_user_retry_count() {
if(device->get_device_model() == DeviceModel::STORAGE){
- stick20::GetDeviceStatus::CommandTransaction::run(*device);
+ stick20::GetDeviceStatus::CommandTransaction::run(device);
}
- auto response = GetUserPasswordRetryCount::CommandTransaction::run(*device);
+ auto response = GetUserPasswordRetryCount::CommandTransaction::run(device);
return response.data().password_retry_count;
}
+
uint8_t NitrokeyManager::get_admin_retry_count() {
if(device->get_device_model() == DeviceModel::STORAGE){
- stick20::GetDeviceStatus::CommandTransaction::run(*device);
+ stick20::GetDeviceStatus::CommandTransaction::run(device);
}
- auto response = GetPasswordRetryCount::CommandTransaction::run(*device);
+ auto response = GetPasswordRetryCount::CommandTransaction::run(device);
return response.data().password_retry_count;
}
void NitrokeyManager::lock_device() {
- LockDevice::CommandTransaction::run(*device);
+ LockDevice::CommandTransaction::run(device);
}
const char *NitrokeyManager::get_password_safe_slot_name(uint8_t slot_number) {
if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
auto p = get_payload<GetPasswordSafeSlotName>();
p.slot_number = slot_number;
- auto response = GetPasswordSafeSlotName::CommandTransaction::run(*device, p);
+ auto response = GetPasswordSafeSlotName::CommandTransaction::run(device, p);
return strdup((const char *) response.data().slot_name);
}
@@ -476,7 +543,7 @@ namespace nitrokey{
if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
auto p = get_payload<GetPasswordSafeSlotLogin>();
p.slot_number = slot_number;
- auto response = GetPasswordSafeSlotLogin::CommandTransaction::run(*device, p);
+ auto response = GetPasswordSafeSlotLogin::CommandTransaction::run(device, p);
return strdup((const char *) response.data().slot_login);
}
@@ -484,8 +551,8 @@ namespace nitrokey{
if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
auto p = get_payload<GetPasswordSafeSlotPassword>();
p.slot_number = slot_number;
- auto response = GetPasswordSafeSlotPassword::CommandTransaction::run(*device, p);
- return strdup((const char *) response.data().slot_password);
+ auto response = GetPasswordSafeSlotPassword::CommandTransaction::run(device, p);
+ return strdup((const char *) response.data().slot_password); //FIXME use secure way
}
void NitrokeyManager::write_password_safe_slot(uint8_t slot_number, const char *slot_name, const char *slot_login,
@@ -495,26 +562,26 @@ namespace nitrokey{
p.slot_number = slot_number;
strcpyT(p.slot_name, slot_name);
strcpyT(p.slot_password, slot_password);
- SetPasswordSafeSlotData::CommandTransaction::run(*device, p);
+ SetPasswordSafeSlotData::CommandTransaction::run(device, p);
auto p2 = get_payload<SetPasswordSafeSlotData2>();
p2.slot_number = slot_number;
strcpyT(p2.slot_login_name, slot_login);
- SetPasswordSafeSlotData2::CommandTransaction::run(*device, p2);
+ SetPasswordSafeSlotData2::CommandTransaction::run(device, p2);
}
void NitrokeyManager::erase_password_safe_slot(uint8_t slot_number) {
if (!is_valid_password_safe_slot_number(slot_number)) throw InvalidSlotException(slot_number);
auto p = get_payload<ErasePasswordSafeSlot>();
p.slot_number = slot_number;
- ErasePasswordSafeSlot::CommandTransaction::run(*device, p);
+ ErasePasswordSafeSlot::CommandTransaction::run(device, p);
}
void NitrokeyManager::user_authenticate(const char *user_password, const char *temporary_password) {
auto p = get_payload<UserAuthenticate>();
strcpyT(p.card_password, user_password);
strcpyT(p.temporary_password, temporary_password);
- UserAuthenticate::CommandTransaction::run(*device, p);
+ UserAuthenticate::CommandTransaction::run(device, p);
}
void NitrokeyManager::build_aes_key(const char *admin_password) {
@@ -522,14 +589,14 @@ namespace nitrokey{
case DeviceModel::PRO: {
auto p = get_payload<BuildAESKey>();
strcpyT(p.admin_password, admin_password);
- BuildAESKey::CommandTransaction::run(*device, p);
+ BuildAESKey::CommandTransaction::run(device, p);
break;
}
case DeviceModel::STORAGE : {
auto p = get_payload<stick20::CreateNewKeys>();
strcpyT(p.password, admin_password);
p.set_defaults();
- stick20::CreateNewKeys::CommandTransaction::run(*device, p);
+ stick20::CreateNewKeys::CommandTransaction::run(device, p);
break;
}
}
@@ -538,7 +605,7 @@ namespace nitrokey{
void NitrokeyManager::factory_reset(const char *admin_password) {
auto p = get_payload<FactoryReset>();
strcpyT(p.admin_password, admin_password);
- FactoryReset::CommandTransaction::run(*device, p);
+ FactoryReset::CommandTransaction::run(device, p);
}
void NitrokeyManager::unlock_user_password(const char *admin_password, const char *new_user_password) {
@@ -547,18 +614,18 @@ namespace nitrokey{
auto p = get_payload<stick10::UnlockUserPassword>();
strcpyT(p.admin_password, admin_password);
strcpyT(p.user_new_password, new_user_password);
- stick10::UnlockUserPassword::CommandTransaction::run(*device, p);
+ stick10::UnlockUserPassword::CommandTransaction::run(device, p);
break;
}
case DeviceModel::STORAGE : {
auto p2 = get_payload<ChangeAdminUserPin20Current>();
p2.set_defaults();
strcpyT(p2.password, admin_password);
- ChangeAdminUserPin20Current::CommandTransaction::run(*device, p2);
+ ChangeAdminUserPin20Current::CommandTransaction::run(device, p2);
auto p3 = get_payload<stick20::UnlockUserPin>();
p3.set_defaults();
strcpyT(p3.password, new_user_password);
- stick20::UnlockUserPin::CommandTransaction::run(*device, p3);
+ stick20::UnlockUserPin::CommandTransaction::run(device, p3);
break;
}
}
@@ -578,11 +645,11 @@ namespace nitrokey{
} else {
strcpyT(p.temporary_admin_password, admin_temporary_password);
}
- stick10_08::WriteGeneralConfig::CommandTransaction::run(*device, p);
+ stick10_08::WriteGeneralConfig::CommandTransaction::run(device, p);
}
vector<uint8_t> NitrokeyManager::read_config() {
- auto responsePayload = GetStatus::CommandTransaction::run(*device);
+ auto responsePayload = GetStatus::CommandTransaction::run(device);
vector<uint8_t> v = vector<uint8_t>(responsePayload.data().general_config,
responsePayload.data().general_config+sizeof(responsePayload.data().general_config));
return v;
@@ -592,20 +659,37 @@ namespace nitrokey{
//authorization command is supported for versions equal or below:
auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({
{DeviceModel::PRO, 7},
- {DeviceModel::STORAGE, 43},
+ {DeviceModel::STORAGE, 999},
+ });
+ return get_minor_firmware_version() <= m[device->get_device_model()];
+ }
+
+ bool NitrokeyManager::is_320_OTP_secret_supported(){
+ //authorization command is supported for versions equal or below:
+ auto m = std::unordered_map<DeviceModel , int, EnumClassHash>({
+ {DeviceModel::PRO, 8},
+ {DeviceModel::STORAGE, 999},
});
- return get_major_firmware_version() <= m[device->get_device_model()];
+ return get_minor_firmware_version() >= m[device->get_device_model()];
+ }
+
+ DeviceModel NitrokeyManager::get_connected_device_model() const{
+ //FIXME throw if no device is connected or return unknown/unconnected value
+ if (device == nullptr){
+ throw std::runtime_error("device not connected");
+ }
+ return device->get_device_model();
}
- int NitrokeyManager::get_major_firmware_version(){
+ int NitrokeyManager::get_minor_firmware_version(){
switch(device->get_device_model()){
case DeviceModel::PRO:{
- auto status_p = GetStatus::CommandTransaction::run(*device);
+ auto status_p = GetStatus::CommandTransaction::run(device);
return status_p.data().firmware_version; //7 or 8
}
case DeviceModel::STORAGE:{
- auto status = stick20::GetDeviceStatus::CommandTransaction::run(*device);
- return status.data().versionInfo.major;
+ auto status = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return status.data().versionInfo.minor;
}
}
return 0;
@@ -614,7 +698,7 @@ namespace nitrokey{
bool NitrokeyManager::is_AES_supported(const char *user_password) {
auto a = get_payload<IsAESSupported>();
strcpyT(a.user_password, user_password);
- IsAESSupported::CommandTransaction::run(*device, a);
+ IsAESSupported::CommandTransaction::run(device, a);
return true;
}
@@ -624,15 +708,15 @@ namespace nitrokey{
auto p = get_payload<stick20::SendStartup>();
// p.set_defaults(); //set current time
p.localtime = seconds_from_epoch;
- stick20::SendStartup::CommandTransaction::run(*device, p);
+ stick20::SendStartup::CommandTransaction::run(device, p);
}
void NitrokeyManager::unlock_encrypted_volume(const char* user_pin){
- misc::execute_password_command<stick20::EnableEncryptedPartition>(*device, user_pin);
+ misc::execute_password_command<stick20::EnableEncryptedPartition>(device, user_pin);
}
void NitrokeyManager::unlock_hidden_volume(const char* hidden_volume_password) {
- misc::execute_password_command<stick20::EnableHiddenEncryptedPartition>(*device, hidden_volume_password);
+ misc::execute_password_command<stick20::EnableHiddenEncryptedPartition>(device, hidden_volume_password);
}
//TODO check is encrypted volume unlocked before execution
@@ -644,52 +728,66 @@ namespace nitrokey{
p.StartBlockPercent_u8 = start_percent;
p.EndBlockPercent_u8 = end_percent;
strcpyT(p.HiddenVolumePassword_au8, hidden_volume_password);
- stick20::SetupHiddenVolume::CommandTransaction::run(*device, p);
+ stick20::SetupHiddenVolume::CommandTransaction::run(device, p);
}
void NitrokeyManager::set_unencrypted_read_only(const char* user_pin) {
- misc::execute_password_command<stick20::SendSetReadonlyToUncryptedVolume>(*device, user_pin);
+ misc::execute_password_command<stick20::SendSetReadonlyToUncryptedVolume>(device, user_pin);
}
void NitrokeyManager::set_unencrypted_read_write(const char* user_pin) {
- misc::execute_password_command<stick20::SendSetReadwriteToUncryptedVolume>(*device, user_pin);
+ misc::execute_password_command<stick20::SendSetReadwriteToUncryptedVolume>(device, user_pin);
}
void NitrokeyManager::export_firmware(const char* admin_pin) {
- misc::execute_password_command<stick20::ExportFirmware>(*device, admin_pin);
+ misc::execute_password_command<stick20::ExportFirmware>(device, admin_pin);
+ }
+
+ void NitrokeyManager::enable_firmware_update(const char* firmware_pin) {
+ misc::execute_password_command<stick20::EnableFirmwareUpdate>(device, firmware_pin);
}
void NitrokeyManager::clear_new_sd_card_warning(const char* admin_pin) {
- misc::execute_password_command<stick20::SendClearNewSdCardFound>(*device, admin_pin);
+ misc::execute_password_command<stick20::SendClearNewSdCardFound>(device, admin_pin);
}
void NitrokeyManager::fill_SD_card_with_random_data(const char* admin_pin) {
auto p = get_payload<stick20::FillSDCardWithRandomChars>();
p.set_defaults();
strcpyT(p.admin_pin, admin_pin);
- stick20::FillSDCardWithRandomChars::CommandTransaction::run(*device, p);
+ stick20::FillSDCardWithRandomChars::CommandTransaction::run(device, p);
}
void NitrokeyManager::change_update_password(const char* current_update_password, const char* new_update_password) {
auto p = get_payload<stick20::ChangeUpdatePassword>();
strcpyT(p.current_update_password, current_update_password);
strcpyT(p.new_update_password, new_update_password);
- stick20::ChangeUpdatePassword::CommandTransaction::run(*device, p);
+ stick20::ChangeUpdatePassword::CommandTransaction::run(device, p);
}
const char * NitrokeyManager::get_status_storage_as_string(){
- auto p = stick20::GetDeviceStatus::CommandTransaction::run(*device);
+ auto p = stick20::GetDeviceStatus::CommandTransaction::run(device);
return strdup(p.data().dissect().c_str());
}
+ stick20::DeviceConfigurationResponsePacket::ResponsePayload NitrokeyManager::get_status_storage(){
+ auto p = stick20::GetDeviceStatus::CommandTransaction::run(device);
+ return p.data();
+ }
+
const char * NitrokeyManager::get_SD_usage_data_as_string(){
- auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(*device);
+ auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device);
return strdup(p.data().dissect().c_str());
}
+ std::pair<uint8_t,uint8_t> NitrokeyManager::get_SD_usage_data(){
+ auto p = stick20::GetSDCardOccupancy::CommandTransaction::run(device);
+ return std::make_pair(p.data().WriteLevelMin, p.data().WriteLevelMax);
+ }
+
int NitrokeyManager::get_progress_bar_value(){
try{
- stick20::GetDeviceStatus::CommandTransaction::run(*device);
+ stick20::GetDeviceStatus::CommandTransaction::run(device);
return -1;
}
catch (LongOperationInProgressException &e){
@@ -697,4 +795,36 @@ namespace nitrokey{
}
}
- }
+ uint32_t NitrokeyManager::get_TOTP_code(uint8_t slot_number, const char *user_temporary_password) {
+ return get_TOTP_code(slot_number, 0, 0, 0, user_temporary_password);
+ }
+
+ stick10::ReadSlot::ResponsePayload NitrokeyManager::get_OTP_slot_data(const uint8_t slot_number) {
+ auto p = get_payload<stick10::ReadSlot>();
+ p.slot_number = slot_number;
+ auto data = stick10::ReadSlot::CommandTransaction::run(device, p);
+ return data.data();
+ }
+
+ stick10::ReadSlot::ResponsePayload NitrokeyManager::get_TOTP_slot_data(const uint8_t slot_number) {
+ return get_OTP_slot_data(get_internal_slot_number_for_totp(slot_number));
+ }
+
+ stick10::ReadSlot::ResponsePayload NitrokeyManager::get_HOTP_slot_data(const uint8_t slot_number) {
+ return get_OTP_slot_data(get_internal_slot_number_for_hotp(slot_number));
+ }
+
+ void NitrokeyManager::lock_encrypted_volume() {
+ misc::execute_password_command<stick20::DisableEncryptedPartition>(device, "");
+ }
+
+ void NitrokeyManager::lock_hidden_volume() {
+ misc::execute_password_command<stick20::DisableHiddenEncryptedPartition>(device, "");
+ }
+
+ uint8_t NitrokeyManager::get_SD_card_size() {
+ auto data = stick20::ProductionTest::CommandTransaction::run(device);
+ return data.data().SD_Card_Size_u8;
+ }
+
+}
diff --git a/device.cc b/device.cc
index 8c3c799..66877da 100644
--- a/device.cc
+++ b/device.cc
@@ -2,81 +2,131 @@
#include <thread>
#include <cstddef>
#include <stdexcept>
-#include <hidapi/hidapi.h>
+#include "hidapi/hidapi.h"
#include "include/misc.h"
#include "include/device.h"
#include "include/log.h"
+#include <mutex>
+#include "DeviceCommunicationExceptions.h"
+
+std::mutex mex_dev_com;
using namespace nitrokey::device;
using namespace nitrokey::log;
+using namespace std::chrono;
+
+std::atomic_int Device::instances_count{0};
-Device::Device()
- : m_vid(0),
- m_pid(0),
+Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
+ const milliseconds send_receive_delay, const int retry_receiving_count,
+ const milliseconds retry_timeout)
+ : m_vid(vid),
+ m_pid(pid),
m_retry_sending_count(3),
- m_retry_receiving_count(40),
- m_retry_timeout(100),
- mp_devhandle(NULL),
- last_command_status(0){}
+ m_retry_receiving_count(retry_receiving_count),
+ m_retry_timeout(retry_timeout),
+ mp_devhandle(nullptr),
+ last_command_status(0),
+ m_model(model),
+ m_send_receive_delay(send_receive_delay)
+{
+ instances_count++;
+}
bool Device::disconnect() {
- Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2);
+ //called in object's destructor
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ return _disconnect();
+}
+
+bool Device::_disconnect() {
+ LOG(std::string(__FUNCTION__) + std::string(m_model == DeviceModel::PRO ? "PRO" : "STORAGE"), Loglevel::DEBUG_L2);
+ LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
+
+ LOG(std::string("Disconnection success: ") + std::to_string(mp_devhandle == nullptr), Loglevel::DEBUG_L2);
+ if(mp_devhandle == nullptr) return false;
- if(mp_devhandle== nullptr) return false;
hid_close(mp_devhandle);
- mp_devhandle = NULL;
- hid_exit();
+ mp_devhandle = nullptr;
+ if (instances_count == 1){
+ LOG(std::string("Calling hid_exit"), Loglevel::DEBUG_L2);
+ hid_exit();
+ }
return true;
}
+
bool Device::connect() {
- Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2);
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ return _connect();
+}
+
+bool Device::_connect() {
+ LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
-// hid_init();
- mp_devhandle = hid_open(m_vid, m_pid, NULL);
- // hid_init();
- return mp_devhandle != NULL;
+// hid_init(); // done automatically on hid_open
+ mp_devhandle = hid_open(m_vid, m_pid, nullptr);
+ const auto success = mp_devhandle != nullptr;
+ LOG(std::string("Connection success: ") + std::to_string(success), Loglevel::DEBUG_L2);
+ return success;
}
int Device::send(const void *packet) {
- Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2);
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
- if (mp_devhandle == NULL)
- throw std::runtime_error("Attempted HID send on an invalid descriptor.");
+ int send_feature_report = -1;
- return (hid_send_feature_report(
- mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE));
+ for (int i = 0; i < 3 && send_feature_report < 0; ++i) {
+ if (mp_devhandle == nullptr) {
+ LOG(std::string("Connection fail") , Loglevel::DEBUG_L2);
+ throw DeviceNotConnected("Attempted HID send on an invalid descriptor.");
+ }
+ send_feature_report = hid_send_feature_report(
+ mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE);
+ if (send_feature_report < 0) _reconnect();
+ //add thread sleep?
+ LOG(std::string("Sending attempt: ")+std::to_string(i) + " / 3" , Loglevel::DEBUG_L2);
+ }
+ return send_feature_report;
}
int Device::recv(void *packet) {
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
int status;
int retry_count = 0;
- Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2);
-
- if (mp_devhandle == NULL)
- throw std::runtime_error("Attempted HID receive on an invalid descriptor.");
-
- // FIXME extract error handling and repeating to parent function in
- // device_proto:192
for (;;) {
+ if (mp_devhandle == nullptr){
+ LOG(std::string("Connection fail") , Loglevel::DEBUG_L2);
+ throw DeviceNotConnected("Attempted HID receive on an invalid descriptor.");
+ }
+
status = (hid_get_feature_report(mp_devhandle, (unsigned char *)(packet),
HID_REPORT_SIZE));
- // FIXME handle getting libhid error message somewhere else
auto pwherr = hid_error(mp_devhandle);
- std::wstring wherr = (pwherr != NULL) ? pwherr : L"No error message";
+ std::wstring wherr = (pwherr != nullptr) ? pwherr : L"No error message";
std::string herr(wherr.begin(), wherr.end());
- Log::instance()(std::string("libhid error message: ") + herr,
+ LOG(std::string("libhid error message: ") + herr,
Loglevel::DEBUG_L2);
if (status > 0) break; // success
if (retry_count++ >= m_retry_receiving_count) {
- Log::instance()(
- "Maximum retry count reached" + std::to_string(retry_count),
+ LOG(
+ "Maximum retry count reached: " + std::to_string(retry_count),
Loglevel::WARNING);
+ LOG(
+ std::string("Counter stats: ") + m_counters.get_as_string(),
+ Loglevel::DEBUG);
break;
}
- Log::instance()("Retrying... " + std::to_string(retry_count),
+ _reconnect();
+ LOG("Retrying... " + std::to_string(retry_count),
Loglevel::DEBUG);
std::this_thread::sleep_for(m_retry_timeout);
}
@@ -84,19 +134,74 @@ int Device::recv(void *packet) {
return status;
}
-Stick10::Stick10() {
- m_vid = 0x20a0;
- m_pid = 0x4108;
- m_model = DeviceModel::PRO;
- m_send_receive_delay = 100ms;
- m_retry_receiving_count = 100;
+bool Device::could_be_enumerated() {
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+ std::lock_guard<std::mutex> lock(mex_dev_com);
+ if (mp_devhandle==nullptr){
+ return false;
+ }
+ auto pInfo = hid_enumerate(m_vid, m_pid);
+ if (pInfo != nullptr){
+ hid_free_enumeration(pInfo);
+ return true;
+ }
+ return false;
+
+// alternative:
+// unsigned char buf[1];
+// return hid_read_timeout(mp_devhandle, buf, sizeof(buf), 20) != -1;
+}
+
+void Device::show_stats() {
+ auto s = m_counters.get_as_string();
+ LOG(s, Loglevel::DEBUG_L2);
+}
+
+void Device::_reconnect() {
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+ ++m_counters.low_level_reconnect;
+ _disconnect();
+ _connect();
+}
+
+Device::~Device() {
+ show_stats();
+ disconnect();
+ instances_count--;
}
-Stick20::Stick20() {
- m_vid = 0x20a0;
- m_pid = 0x4109;
- m_retry_timeout = 200ms;
- m_model = DeviceModel::STORAGE;
- m_send_receive_delay = 200ms;
- m_retry_receiving_count = 40;
+Stick10::Stick10():
+ Device(0x20a0, 0x4108, DeviceModel::PRO, 100ms, 5, 100ms)
+ {}
+
+
+Stick20::Stick20():
+ Device(0x20a0, 0x4109, DeviceModel::STORAGE, 20ms, 20, 20ms)
+ {}
+
+#include <sstream>
+#define p(x) ss << #x << " " << x << ", ";
+std::string Device::ErrorCounters::get_as_string() {
+ std::stringstream ss;
+ p(total_comm_runs);
+ p(communication_successful);
+ ss << "(";
+ p(command_successful_recv);
+ p(command_result_not_equal_0_recv);
+ ss << "), ";
+ p(sends_executed);
+ p(recv_executed);
+ p(successful_storage_commands);
+ p(total_retries);
+ ss << "(";
+ p(busy);
+ p(busy_progressbar);
+ p(CRC_other_than_awaited);
+ p(wrong_CRC);
+ ss << "), ";
+ p(low_level_reconnect);
+ p(sending_error);
+ p(receiving_error);
+ return ss.str();
}
+#undef p \ No newline at end of file
diff --git a/hidapi b/hidapi
new file mode 160000
+Subproject 69c45b083821037667a9409b952282f9cb4dcca
diff --git a/include/CommandFailedException.h b/include/CommandFailedException.h
index 9b0c59e..417e850 100644
--- a/include/CommandFailedException.h
+++ b/include/CommandFailedException.h
@@ -7,23 +7,47 @@
#include <exception>
#include <cstdint>
-#include <log.h>
+#include "log.h"
+#include "command_id.h"
+
+using cs = nitrokey::proto::stick10::command_status;
class CommandFailedException : public std::exception {
public:
- uint8_t last_command_code;
- uint8_t last_command_status;
+ const uint8_t last_command_id;
+ const uint8_t last_command_status;
- CommandFailedException(uint8_t last_command_code, uint8_t last_command_status) :
- last_command_code(last_command_code),
+ CommandFailedException(uint8_t last_command_id, uint8_t last_command_status) :
+ last_command_id(last_command_id),
last_command_status(last_command_status){
- nitrokey::log::Log::instance()(std::string("CommandFailedException, status: ")+ std::to_string(last_command_status), nitrokey::log::Loglevel::DEBUG);
+ LOG(std::string("CommandFailedException, status: ")+ std::to_string(last_command_status), nitrokey::log::Loglevel::DEBUG);
}
virtual const char *what() const throw() {
return "Command execution has failed on device";
}
+
+ bool reason_timestamp_warning() const throw(){
+ return last_command_status == static_cast<uint8_t>(cs::timestamp_warning);
+ }
+
+ bool reason_AES_not_initialized() const throw(){
+ return last_command_status == static_cast<uint8_t>(cs::AES_dec_failed);
+ }
+
+ bool reason_not_authorized() const throw(){
+ return last_command_status == static_cast<uint8_t>(cs::not_authorized);
+ }
+
+ bool reason_slot_not_programmed() const throw(){
+ return last_command_status == static_cast<uint8_t>(cs::slot_not_programmed);
+ }
+
+ bool reason_wrong_password() const throw(){
+ return last_command_status == static_cast<uint8_t>(cs::wrong_password);
+ }
+
};
diff --git a/include/DeviceCommunicationExceptions.h b/include/DeviceCommunicationExceptions.h
new file mode 100644
index 0000000..6d21561
--- /dev/null
+++ b/include/DeviceCommunicationExceptions.h
@@ -0,0 +1,40 @@
+#ifndef LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H
+#define LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H
+
+#include <atomic>
+#include <exception>
+#include <stdexcept>
+#include <string>
+
+class DeviceCommunicationException: public std::runtime_error
+{
+ std::string message;
+ static std::atomic_int occurred;
+public:
+ DeviceCommunicationException(std::string _msg): std::runtime_error(_msg), message(_msg){
+ ++occurred;
+ }
+// virtual const char* what() const throw() override {
+// return message.c_str();
+// }
+ static bool has_occurred(){ return occurred > 0; };
+ static void reset_occurred_flag(){ occurred = 0; };
+};
+
+class DeviceNotConnected: public DeviceCommunicationException {
+public:
+ DeviceNotConnected(std::string msg) : DeviceCommunicationException(msg){}
+};
+
+class DeviceSendingFailure: public DeviceCommunicationException {
+public:
+ DeviceSendingFailure(std::string msg) : DeviceCommunicationException(msg){}
+};
+
+class DeviceReceivingFailure: public DeviceCommunicationException {
+public:
+ DeviceReceivingFailure(std::string msg) : DeviceCommunicationException(msg){}
+};
+
+
+#endif //LIBNITROKEY_DEVICECOMMUNICATIONEXCEPTIONS_H
diff --git a/include/LibraryException.h b/include/LibraryException.h
index 3c3fab4..b9303ad 100644
--- a/include/LibraryException.h
+++ b/include/LibraryException.h
@@ -11,8 +11,6 @@ public:
virtual uint8_t exception_id()= 0;
};
-
-
class TargetBufferSmallerThanSource: public LibraryException {
public:
virtual uint8_t exception_id() override {
@@ -29,7 +27,7 @@ public:
virtual const char *what() const throw() override {
std::string s = " ";
- auto ts = [](int x){ return std::to_string(x); };
+ auto ts = [](size_t x){ return std::to_string(x); };
std::string msg = std::string("Target buffer size is smaller than source: [source size, buffer size]")
+s+ ts(source_size) +s+ ts(target_size);
return msg.c_str();
@@ -85,7 +83,7 @@ public:
TooLongStringException(size_t size_source, size_t size_destination, const std::string &message = "") : size_source(
size_source), size_destination(size_destination), message(message) {
- nitrokey::log::Log::instance()(std::string("TooLongStringException, size diff: ")+ std::to_string(size_source-size_destination), nitrokey::log::Loglevel::DEBUG);
+ LOG(std::string("TooLongStringException, size diff: ")+ std::to_string(size_source-size_destination), nitrokey::log::Loglevel::DEBUG);
}
diff --git a/include/LongOperationInProgressException.h b/include/LongOperationInProgressException.h
index 7f182b0..5b441c0 100644
--- a/include/LongOperationInProgressException.h
+++ b/include/LongOperationInProgressException.h
@@ -15,7 +15,7 @@ public:
LongOperationInProgressException(
unsigned char _command_id, uint8_t last_command_status, unsigned char _progress_bar_value)
: CommandFailedException(_command_id, last_command_status), progress_bar_value(_progress_bar_value){
- nitrokey::log::Log::instance()(
+ LOG(
std::string("LongOperationInProgressException, progress bar status: ")+
std::to_string(progress_bar_value), nitrokey::log::Loglevel::DEBUG);
}
diff --git a/include/NitrokeyManager.h b/include/NitrokeyManager.h
index fd39445..4f11314 100644
--- a/include/NitrokeyManager.h
+++ b/include/NitrokeyManager.h
@@ -32,22 +32,31 @@ namespace nitrokey {
uint32_t get_HOTP_code(uint8_t slot_number, const char *user_temporary_password);
uint32_t get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, uint8_t last_interval,
const char *user_temporary_password);
+ uint32_t get_TOTP_code(uint8_t slot_number, const char *user_temporary_password);
+ stick10::ReadSlot::ResponsePayload get_TOTP_slot_data(const uint8_t slot_number);
+ stick10::ReadSlot::ResponsePayload get_HOTP_slot_data(const uint8_t slot_number);
+
bool set_time(uint64_t time);
- bool get_time();
+ bool get_time(uint64_t time = 0);
bool erase_totp_slot(uint8_t slot_number, const char *temporary_password);
bool erase_hotp_slot(uint8_t slot_number, const char *temporary_password);
bool connect(const char *device_model);
bool connect();
bool disconnect();
- void set_debug(bool state);
- string get_status();
+ bool is_connected() throw() ;
+ bool could_current_device_be_enumerated();
+
+ DeviceModel get_connected_device_model() const;
+ void set_debug(bool state);
+ stick10::GetStatus::ResponsePayload get_status();
+ string get_status_as_string();
string get_serial_number();
const char * get_totp_slot_name(uint8_t slot_number);
const char * get_hotp_slot_name(uint8_t slot_number);
- void change_user_PIN(char *current_PIN, char *new_PIN);
- void change_admin_PIN(char *current_PIN, char *new_PIN);
+ void change_user_PIN(const char *current_PIN, const char *new_PIN);
+ void change_admin_PIN(const char *current_PIN, const char *new_PIN);
void enable_password_safe(const char *user_pin);
@@ -84,19 +93,24 @@ namespace nitrokey {
bool is_AES_supported(const char *user_password);
void unlock_encrypted_volume(const char *user_password);
+ void lock_encrypted_volume();
void unlock_hidden_volume(const char *hidden_volume_password);
+ void lock_hidden_volume();
void set_unencrypted_read_only(const char *user_pin);
void set_unencrypted_read_write(const char *user_pin);
void export_firmware(const char *admin_pin);
+ void enable_firmware_update(const char *firmware_pin);
void clear_new_sd_card_warning(const char *admin_pin);
void fill_SD_card_with_random_data(const char *admin_pin);
+ uint8_t get_SD_card_size();
+
void change_update_password(const char *current_update_password, const char *new_update_password);
void create_hidden_volume(uint8_t slot_nr, uint8_t start_percent, uint8_t end_percent,
@@ -105,26 +119,31 @@ namespace nitrokey {
void send_startup(uint64_t seconds_from_epoch);
const char * get_status_storage_as_string();
+ stick20::DeviceConfigurationResponsePacket::ResponsePayload get_status_storage();
const char *get_SD_usage_data_as_string();
+ std::pair<uint8_t,uint8_t> get_SD_usage_data();
+
int get_progress_bar_value();
~NitrokeyManager();
bool is_authorization_command_supported();
+ bool is_320_OTP_secret_supported();
+
- template <typename S, typename A, typename T>
+ template <typename S, typename A, typename T>
void authorize_packet(T &package, const char *admin_temporary_password, shared_ptr<Device> device);
- int get_major_firmware_version();
+ int get_minor_firmware_version();
+ explicit NitrokeyManager();
private:
- NitrokeyManager();
static shared_ptr <NitrokeyManager> _instance;
- bool connected;
std::shared_ptr<Device> device;
- bool is_valid_hotp_slot_number(uint8_t slot_number) const;
+ stick10::ReadSlot::ResponsePayload get_OTP_slot_data(const uint8_t slot_number);
+ bool is_valid_hotp_slot_number(uint8_t slot_number) const;
bool is_valid_totp_slot_number(uint8_t slot_number) const;
bool is_valid_password_safe_slot_number(uint8_t slot_number) const;
uint8_t get_internal_slot_number_for_hotp(uint8_t slot_number) const;
@@ -133,7 +152,7 @@ namespace nitrokey {
const char * get_slot_name(uint8_t slot_number);
template <typename ProCommand, PasswordKind StoKind>
- void change_PIN_general(char *current_PIN, char *new_PIN);
+ void change_PIN_general(const char *current_PIN, const char *new_PIN);
void write_HOTP_slot_authorize(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
@@ -148,6 +167,8 @@ namespace nitrokey {
bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID,
const char *temporary_password) const;
+ bool _disconnect_no_lock();
+
};
}
diff --git a/include/command.h b/include/command.h
index 0a875e4..fc374f7 100644
--- a/include/command.h
+++ b/include/command.h
@@ -28,6 +28,7 @@ namespace stick20{
template<CommandID cmd_id, PasswordKind Tpassword_kind = PasswordKind::User, int password_length = 20>
class PasswordCommand : public Command<cmd_id> {
+ constexpr static CommandID _command_id() { return cmd_id; }
public:
struct CommandPayload {
uint8_t kind;
@@ -69,8 +70,10 @@ namespace stick20{
} __packed;
- typedef Transaction<Command<cmd_id>::command_id(), struct CommandPayload, struct EmptyPayload>
- CommandTransaction;
+ //typedef Transaction<Command<cmd_id>::command_id(), struct CommandPayload, struct EmptyPayload>
+ // CommandTransaction;
+ using CommandTransaction = Transaction<cmd_id, CommandPayload, EmptyPayload>;
+ //using CommandTransaction = Transaction<_command_id(), CommandPayload, EmptyPayload>;
};
}
diff --git a/include/command_id.h b/include/command_id.h
index 346b750..d1246dd 100644
--- a/include/command_id.h
+++ b/include/command_id.h
@@ -1,6 +1,6 @@
#ifndef COMMAND_ID_H
#define COMMAND_ID_H
-#include "inttypes.h"
+#include <stdint.h>
namespace nitrokey {
namespace proto {
@@ -12,7 +12,7 @@ namespace proto {
wrong_password,
busy_progressbar,
password_matrix_ready,
- no_user_password_unlock,
+ no_user_password_unlock, // FIXME: translate on receive to command status error (fix in firmware?)
smartcard_error,
security_bit_active
};
@@ -69,9 +69,9 @@ enum class CommandID : uint8_t {
SEND_OTP_DATA = 0x17,
ENABLE_CRYPTED_PARI = 0x20,
- DISABLE_CRYPTED_PARI = 0x20 + 1, //@unused
+ DISABLE_CRYPTED_PARI = 0x20 + 1,
ENABLE_HIDDEN_CRYPTED_PARI = 0x20 + 2,
- DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3, //@unused
+ DISABLE_HIDDEN_CRYPTED_PARI = 0x20 + 3,
ENABLE_FIRMWARE_UPDATE = 0x20 + 4, //enables update mode
EXPORT_FIRMWARE_TO_FILE = 0x20 + 5,
GENERATE_NEW_KEYS = 0x20 + 6,
diff --git a/include/cxx_semantics.h b/include/cxx_semantics.h
index b846317..f358e8f 100644
--- a/include/cxx_semantics.h
+++ b/include/cxx_semantics.h
@@ -1,7 +1,15 @@
#ifndef CXX_SEMANTICS_H
#define CXX_SEMANTICS_H
+#ifndef _MSC_VER
#define __packed __attribute__((__packed__))
+#else
+#define __packed
+#endif
+
+#ifdef _MSC_VER
+#define strdup _strdup
+#endif
/*
* There's no need to include Boost for a simple subset this project needs.
diff --git a/include/device.h b/include/device.h
index 62c4073..7b300e5 100644
--- a/include/device.h
+++ b/include/device.h
@@ -1,16 +1,18 @@
#ifndef DEVICE_H
#define DEVICE_H
#include <chrono>
-#include <hidapi/hidapi.h>
-#include "inttypes.h"
+#include "hidapi/hidapi.h"
+#include <cstdint>
+#include <string>
#define HID_REPORT_SIZE 65
-// TODO !! SEMAPHORE
+#include <atomic>
namespace nitrokey {
namespace device {
using namespace std::chrono_literals;
+ using std::chrono::milliseconds;
struct EnumClassHash
{
@@ -26,11 +28,39 @@ enum class DeviceModel{
STORAGE
};
+#include <atomic>
+
class Device {
public:
- Device();
- virtual ~Device(){disconnect();}
+
+ struct ErrorCounters{
+ using cnt = std::atomic_int;
+ cnt wrong_CRC;
+ cnt CRC_other_than_awaited;
+ cnt busy;
+ cnt total_retries;
+ cnt sending_error;
+ cnt receiving_error;
+ cnt total_comm_runs;
+ cnt successful_storage_commands;
+ cnt command_successful_recv;
+ cnt recv_executed;
+ cnt sends_executed;
+ cnt busy_progressbar;
+ cnt command_result_not_equal_0_recv;
+ cnt communication_successful;
+ cnt low_level_reconnect;
+ std::string get_as_string();
+
+ } m_counters = {};
+
+
+ Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
+ const milliseconds send_receive_delay, const int retry_receiving_count,
+ const milliseconds retry_timeout);
+
+ virtual ~Device();
// lack of device is not actually an error,
// so it doesn't throw
@@ -48,34 +78,49 @@ public:
*/
virtual int recv(void *packet);
+ /***
+ * Returns true if some device is visible by OS with given VID and PID
+ * whether the device is connected through HID API or not.
+ * @return true if visible by OS
+ */
+ bool could_be_enumerated();
+
+ void show_stats();
+// ErrorCounters get_stats(){ return m_counters; }
int get_retry_receiving_count() const { return m_retry_receiving_count; };
int get_retry_sending_count() const { return m_retry_sending_count; };
std::chrono::milliseconds get_retry_timeout() const { return m_retry_timeout; };
- std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;}
+ std::chrono::milliseconds get_send_receive_delay() const {return m_send_receive_delay;}
- int get_last_command_status() {auto a = last_command_status; last_command_status = 0; return a;};
- void set_last_command_status(uint8_t _err) { last_command_status = _err;} ;
- bool last_command_sucessfull() const {return last_command_status == 0;};
- DeviceModel get_device_model() const {return m_model;}
+ int get_last_command_status() {int a = std::atomic_exchange(&last_command_status, static_cast<uint8_t>(0)); return a;};
+ void set_last_command_status(uint8_t _err) { last_command_status = _err;} ;
+ bool last_command_sucessfull() const {return last_command_status == 0;};
+ DeviceModel get_device_model() const {return m_model;}
private:
- uint8_t last_command_status;
+ std::atomic<uint8_t> last_command_status;
+ void _reconnect();
+ bool _connect();
+ bool _disconnect();
- protected:
- uint16_t m_vid;
- uint16_t m_pid;
- DeviceModel m_model;
+protected:
+ const uint16_t m_vid;
+ const uint16_t m_pid;
+ const DeviceModel m_model;
/*
* While the project uses Signal11 portable HIDAPI
* library, there's no way of doing it asynchronously,
* hence polling.
*/
- int m_retry_sending_count;
- int m_retry_receiving_count;
- std::chrono::milliseconds m_retry_timeout;
- std::chrono::milliseconds m_send_receive_delay;
+ const int m_retry_sending_count;
+ const int m_retry_receiving_count;
+ const std::chrono::milliseconds m_retry_timeout;
+ const std::chrono::milliseconds m_send_receive_delay;
+
+ std::atomic<hid_device *>mp_devhandle;
+
- hid_device *mp_devhandle;
+ static std::atomic_int instances_count;
};
class Stick10 : public Device {
diff --git a/include/device_proto.h b/include/device_proto.h
index 0953566..b137689 100644
--- a/include/device_proto.h
+++ b/include/device_proto.h
@@ -6,9 +6,8 @@
#include <type_traits>
#include <stdexcept>
#include <string>
-#include <strings.h>
// a local version for compatibility with Windows
-#include "inttypes.h"
+#include <stdint.h>
#include "cxx_semantics.h"
#include "device.h"
#include "misc.h"
@@ -32,6 +31,10 @@
#define PWS_SEND_TAB 2
#define PWS_SEND_CR 3
+#include <mutex>
+#include "DeviceCommunicationExceptions.h"
+#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
+
namespace nitrokey {
namespace proto {
/*
@@ -40,7 +43,7 @@ namespace nitrokey {
*
* TODO (future) support for Big Endian
*/
-
+#pragma pack (push,1)
/*
* Every packet is a USB HID report (check USB spec)
*/
@@ -178,8 +181,10 @@ namespace nitrokey {
typedef command_payload CommandPayload;
typedef response_payload ResponsePayload;
+
typedef struct HIDReport<cmd_id, CommandPayload> OutgoingPacket;
typedef struct DeviceResponse<cmd_id, ResponsePayload> ResponsePacket;
+#pragma pack (pop)
static_assert(std::is_pod<OutgoingPacket>::value,
"outgoingpacket must be a pod type");
@@ -204,14 +209,21 @@ namespace nitrokey {
bzero(&st, sizeof(st));
}
-
- static ClearingProxy<ResponsePacket, response_payload> run(device::Device &dev,
+ static ClearingProxy<ResponsePacket, response_payload> run(std::shared_ptr<device::Device> dev,
const command_payload &payload) {
using namespace ::nitrokey::device;
using namespace ::nitrokey::log;
using namespace std::chrono_literals;
- Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2);
+ static std::mutex send_receive_mtx;
+ std::lock_guard<std::mutex> guard(send_receive_mtx);
+
+ LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+
+ if (dev == nullptr){
+ throw DeviceNotConnected("Device not initialized");
+ }
+ dev->m_counters.total_comm_runs++;
int status;
OutgoingPacket outp;
@@ -224,32 +236,41 @@ namespace nitrokey {
outp.payload = payload;
outp.update_CRC();
- Log::instance()("Outgoing HID packet:", Loglevel::DEBUG);
- Log::instance()(static_cast<std::string>(outp), Loglevel::DEBUG);
+ LOG("Outgoing HID packet:", Loglevel::DEBUG);
+ LOG(static_cast<std::string>(outp), Loglevel::DEBUG);
- if (!outp.isValid()) throw std::runtime_error("Invalid outgoing packet");
+ if (!outp.isValid()) throw DeviceSendingFailure("Invalid outgoing packet");
bool successful_communication = false;
int receiving_retry_counter = 0;
- int sending_retry_counter = dev.get_retry_sending_count();
+ int sending_retry_counter = dev->get_retry_sending_count();
while (sending_retry_counter-- > 0) {
- status = dev.send(&outp);
- if (status <= 0)
- throw std::runtime_error(
+ dev->m_counters.sends_executed++;
+ status = dev->send(&outp);
+ if (status <= 0){
+ //FIXME early disconnection not yet working properly
+// LOG("Encountered communication error, disconnecting device", Loglevel::DEBUG_L2);
+// dev->disconnect();
+ dev->m_counters.sending_error++;
+ throw DeviceSendingFailure(
std::string("Device error while sending command ") +
std::to_string(status));
+ }
- std::this_thread::sleep_for(dev.get_send_receive_delay());
+ std::this_thread::sleep_for(dev->get_send_receive_delay());
// FIXME make checks done in device:recv here
- receiving_retry_counter = dev.get_retry_receiving_count();
+ receiving_retry_counter = dev->get_retry_receiving_count();
+ int busy_counter = 0;
+ auto retry_timeout = dev->get_retry_timeout();
while (receiving_retry_counter-- > 0) {
- status = dev.recv(&resp);
+ dev->m_counters.recv_executed++;
+ status = dev->recv(&resp);
- if (dev.get_device_model() == DeviceModel::STORAGE &&
+ if (dev->get_device_model() == DeviceModel::STORAGE &&
resp.command_id >= stick20::CMD_START_VALUE &&
resp.command_id < stick20::CMD_END_VALUE ) {
- Log::instance()(std::string("Detected storage device cmd, status: ") +
+ LOG(std::string("Detected storage device cmd, status: ") +
std::to_string(resp.storage_status.device_status), Loglevel::DEBUG_L2);
resp.last_command_status = static_cast<uint8_t>(stick10::command_status::ok);
@@ -267,7 +288,7 @@ namespace nitrokey {
resp.device_status = static_cast<uint8_t>(stick10::device_status::ok);
break;
default:
- Log::instance()(std::string("Unknown storage device status, cannot translate: ") +
+ LOG(std::string("Unknown storage device status, cannot translate: ") +
std::to_string(resp.storage_status.device_status), Loglevel::DEBUG);
resp.device_status = resp.storage_status.device_status;
break;
@@ -276,15 +297,25 @@ namespace nitrokey {
//SENDPASSWORD gives wrong CRC , for now rely on !=0 (TODO report)
// if (resp.device_status == 0 && resp.last_command_crc == outp.crc && resp.isCRCcorrect()) break;
+ auto CRC_equal_awaited = resp.last_command_crc == outp.crc;
if (resp.device_status == static_cast<uint8_t>(stick10::device_status::ok) &&
- resp.last_command_crc == outp.crc && resp.isValid()){
+ CRC_equal_awaited && resp.isValid()){
successful_communication = true;
break;
}
if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy)) {
- receiving_retry_counter++;
- Log::instance()("Status busy, not decresing receiving_retry_counter counter: " +
- std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2);
+ dev->m_counters.busy++;
+ if (busy_counter++<10) {
+ receiving_retry_counter++;
+ LOG("Status busy, not decreasing receiving_retry_counter counter: " +
+ std::to_string(receiving_retry_counter), Loglevel::DEBUG_L2);
+ } else {
+ retry_timeout *= 2;
+ busy_counter = 0;
+ LOG("Status busy, decreasing receiving_retry_counter counter: " +
+ std::to_string(receiving_retry_counter) + ", current delay:"
+ + std::to_string(retry_timeout.count()), Loglevel::DEBUG);
+ }
}
if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy) &&
static_cast<stick20::device_status>(resp.storage_status.device_status)
@@ -292,59 +323,78 @@ namespace nitrokey {
successful_communication = true;
break;
}
- Log::instance()(std::string("Retry status - dev status, equal crc, correct CRC: ")
+ LOG(std::string("Retry status - dev status, awaited cmd crc, correct packet CRC: ")
+ std::to_string(resp.device_status) + " " +
- std::to_string(resp.last_command_crc == outp.crc) +
+ std::to_string(CRC_equal_awaited) +
" " + std::to_string(resp.isCRCcorrect()), Loglevel::DEBUG_L2);
- Log::instance()(
+ if (!resp.isCRCcorrect()) dev->m_counters.wrong_CRC++;
+ if (!CRC_equal_awaited) dev->m_counters.CRC_other_than_awaited++;
+
+
+ LOG(
"Device is not ready or received packet's last CRC is not equal to sent CRC packet, retrying...",
Loglevel::DEBUG);
- Log::instance()("Invalid incoming HID packet:", Loglevel::DEBUG_L2);
- Log::instance()(static_cast<std::string>(resp), Loglevel::DEBUG_L2);
- std::this_thread::sleep_for(dev.get_retry_timeout());
+ LOG("Invalid incoming HID packet:", Loglevel::DEBUG_L2);
+ LOG(static_cast<std::string>(resp), Loglevel::DEBUG_L2);
+ dev->m_counters.total_retries++;
+ std::this_thread::sleep_for(retry_timeout);
continue;
}
if (successful_communication) break;
- Log::instance()(std::string("Resending (outer loop) "), Loglevel::DEBUG_L2);
- Log::instance()(std::string("sending_retry_counter count: ") + std::to_string(sending_retry_counter),
+ LOG(std::string("Resending (outer loop) "), Loglevel::DEBUG_L2);
+ LOG(std::string("sending_retry_counter count: ") + std::to_string(sending_retry_counter),
Loglevel::DEBUG);
}
- dev.set_last_command_status(resp.last_command_status); // FIXME should be handled on device.recv
+ dev->set_last_command_status(resp.last_command_status); // FIXME should be handled on device.recv
clear_packet(outp);
- if (status <= 0)
- throw std::runtime_error( //FIXME replace with CriticalErrorException
+ if (status <= 0) {
+ dev->m_counters.receiving_error++;
+ throw DeviceReceivingFailure( //FIXME replace with CriticalErrorException
std::string("Device error while executing command ") +
std::to_string(status));
+ }
- Log::instance()("Incoming HID packet:", Loglevel::DEBUG);
- Log::instance()(static_cast<std::string>(resp), Loglevel::DEBUG);
- Log::instance()(std::string("receiving_retry_counter count: ") + std::to_string(receiving_retry_counter),
+ LOG("Incoming HID packet:", Loglevel::DEBUG);
+ LOG(static_cast<std::string>(resp), Loglevel::DEBUG);
+ LOG(std::string("receiving_retry_counter count: ") + std::to_string(receiving_retry_counter),
Loglevel::DEBUG);
if (resp.device_status == static_cast<uint8_t>(stick10::device_status::busy) &&
static_cast<stick20::device_status>(resp.storage_status.device_status)
== stick20::device_status::busy_progressbar){
+ dev->m_counters.busy_progressbar++;
throw LongOperationInProgressException(
resp.command_id, resp.device_status, resp.storage_status.progress_bar_value);
}
- if (!resp.isValid()) throw std::runtime_error("Invalid incoming packet");
+ if (!resp.isValid()) throw DeviceReceivingFailure("Invalid incoming packet");
if (receiving_retry_counter <= 0)
- throw std::runtime_error(
+ throw DeviceReceivingFailure(
"Maximum receiving_retry_counter count reached for receiving response from the device!");
- if (resp.last_command_status != static_cast<uint8_t>(stick10::command_status::ok))
+ dev->m_counters.communication_successful++;
+
+ if (resp.last_command_status != static_cast<uint8_t>(stick10::command_status::ok)){
+ dev->m_counters.command_result_not_equal_0_recv++;
throw CommandFailedException(resp.command_id, resp.last_command_status);
+ }
+
+ dev->m_counters.command_successful_recv++;
+ if (dev->get_device_model() == DeviceModel::STORAGE &&
+ resp.command_id >= stick20::CMD_START_VALUE &&
+ resp.command_id < stick20::CMD_END_VALUE ) {
+ dev->m_counters.successful_storage_commands++;
+ }
// See: DeviceResponse
return resp;
}
- static ClearingProxy<ResponsePacket, response_payload> run(device::Device &dev) {
+ static ClearingProxy<ResponsePacket, response_payload> run(std::shared_ptr<device::Device> dev) {
command_payload empty_payload;
return run(dev, empty_payload);
}
diff --git a/include/dissect.h b/include/dissect.h
index 8c975c5..8992c56 100644
--- a/include/dissect.h
+++ b/include/dissect.h
@@ -98,7 +98,8 @@ class ResponseDissector : semantics::non_constructible {
out << "CRC:\t"
<< std::hex << std::setw(2) << std::setfill('0')
<< pod.crc << std::endl;
- out << "Storage stick status:" << std::endl;
+ if((int)pod.command_id == pod.storage_status.command_id){
+ out << "Storage stick status (where applicable):" << std::endl;
#define d(x) out << " "#x": \t"<< std::hex << std::setw(2) \
<< std::setfill('0')<< static_cast<int>(x) << std::endl;
d(pod.storage_status.command_counter);
@@ -106,6 +107,7 @@ class ResponseDissector : semantics::non_constructible {
d(pod.storage_status.device_status);
d(pod.storage_status.progress_bar_value);
#undef d
+ }
out << "Payload:" << std::endl;
out << pod.payload.dissect();
diff --git a/include/hidapi/hidapi.h b/include/hidapi/hidapi.h
new file mode 100644
index 0000000..e5bc2dc
--- /dev/null
+++ b/include/hidapi/hidapi.h
@@ -0,0 +1,391 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Alan Ott
+ Signal 11 Software
+
+ 8/22/2009
+
+ Copyright 2009, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ http://github.com/signal11/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+ */
+
+#ifndef HIDAPI_H__
+#define HIDAPI_H__
+
+#include <wchar.h>
+
+#ifdef _WIN32
+ #define HID_API_EXPORT __declspec(dllexport)
+ #define HID_API_CALL
+#else
+ #define HID_API_EXPORT /**< API export macro */
+ #define HID_API_CALL /**< API call macro */
+#endif
+
+#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ struct hid_device_;
+ typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
+
+ /** hidapi info structure */
+ struct hid_device_info {
+ /** Platform-specific device path */
+ char *path;
+ /** Device Vendor ID */
+ unsigned short vendor_id;
+ /** Device Product ID */
+ unsigned short product_id;
+ /** Serial Number */
+ wchar_t *serial_number;
+ /** Device Release Number in binary-coded decimal,
+ also known as Device Version Number */
+ unsigned short release_number;
+ /** Manufacturer String */
+ wchar_t *manufacturer_string;
+ /** Product string */
+ wchar_t *product_string;
+ /** Usage Page for this Device/Interface
+ (Windows/Mac only). */
+ unsigned short usage_page;
+ /** Usage for this Device/Interface
+ (Windows/Mac only).*/
+ unsigned short usage;
+ /** The USB interface which this logical device
+ represents. Valid on both Linux implementations
+ in all cases, and valid on the Windows implementation
+ only if the device contains more than one interface. */
+ int interface_number;
+
+ /** Pointer to the next device */
+ struct hid_device_info *next;
+ };
+
+
+ /** @brief Initialize the HIDAPI library.
+
+ This function initializes the HIDAPI library. Calling it is not
+ strictly necessary, as it will be called automatically by
+ hid_enumerate() and any of the hid_open_*() functions if it is
+ needed. This function should be called at the beginning of
+ execution however, if there is a chance of HIDAPI handles
+ being opened by different threads simultaneously.
+
+ @ingroup API
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_init(void);
+
+ /** @brief Finalize the HIDAPI library.
+
+ This function frees all of the static data associated with
+ HIDAPI. It should be called at the end of execution to avoid
+ memory leaks.
+
+ @ingroup API
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_exit(void);
+
+ /** @brief Enumerate the HID Devices.
+
+ This function returns a linked list of all the HID devices
+ attached to the system which match vendor_id and product_id.
+ If @p vendor_id is set to 0 then any vendor matches.
+ If @p product_id is set to 0 then any product matches.
+ If @p vendor_id and @p product_id are both set to 0, then
+ all HID devices will be returned.
+
+ @ingroup API
+ @param vendor_id The Vendor ID (VID) of the types of device
+ to open.
+ @param product_id The Product ID (PID) of the types of
+ device to open.
+
+ @returns
+ This function returns a pointer to a linked list of type
+ struct #hid_device, containing information about the HID devices
+ attached to the system, or NULL in the case of failure. Free
+ this linked list by calling hid_free_enumeration().
+ */
+ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
+
+ /** @brief Free an enumeration Linked List
+
+ This function frees a linked list created by hid_enumerate().
+
+ @ingroup API
+ @param devs Pointer to a list of struct_device returned from
+ hid_enumerate().
+ */
+ void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
+
+ /** @brief Open a HID device using a Vendor ID (VID), Product ID
+ (PID) and optionally a serial number.
+
+ If @p serial_number is NULL, the first device with the
+ specified VID and PID is opened.
+
+ @ingroup API
+ @param vendor_id The Vendor ID (VID) of the device to open.
+ @param product_id The Product ID (PID) of the device to open.
+ @param serial_number The Serial Number of the device to open
+ (Optionally NULL).
+
+ @returns
+ This function returns a pointer to a #hid_device object on
+ success or NULL on failure.
+ */
+ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
+
+ /** @brief Open a HID device by its path name.
+
+ The path name be determined by calling hid_enumerate(), or a
+ platform-specific path name can be used (eg: /dev/hidraw0 on
+ Linux).
+
+ @ingroup API
+ @param path The path name of the device to open
+
+ @returns
+ This function returns a pointer to a #hid_device object on
+ success or NULL on failure.
+ */
+ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
+
+ /** @brief Write an Output report to a HID device.
+
+ The first byte of @p data[] must contain the Report ID. For
+ devices which only support a single report, this must be set
+ to 0x0. The remaining bytes contain the report data. Since
+ the Report ID is mandatory, calls to hid_write() will always
+ contain one more byte than the report contains. For example,
+ if a hid report is 16 bytes long, 17 bytes must be passed to
+ hid_write(), the Report ID (or 0x0, for devices with a
+ single report), followed by the report data (16 bytes). In
+ this example, the length passed in would be 17.
+
+ hid_write() will send the data on the first OUT endpoint, if
+ one exists. If it does not, it will send the data through
+ the Control Endpoint (Endpoint 0).
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param data The data to send, including the report number as
+ the first byte.
+ @param length The length in bytes of the data to send.
+
+ @returns
+ This function returns the actual number of bytes written and
+ -1 on error.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
+
+ /** @brief Read an Input report from a HID device with timeout.
+
+ Input reports are returned
+ to the host through the INTERRUPT IN endpoint. The first byte will
+ contain the Report number if the device uses numbered reports.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param data A buffer to put the read data into.
+ @param length The number of bytes to read. For devices with
+ multiple reports, make sure to read an extra byte for
+ the report number.
+ @param milliseconds timeout in milliseconds or -1 for blocking wait.
+
+ @returns
+ This function returns the actual number of bytes read and
+ -1 on error. If no packet was available to be read within
+ the timeout period, this function returns 0.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
+
+ /** @brief Read an Input report from a HID device.
+
+ Input reports are returned
+ to the host through the INTERRUPT IN endpoint. The first byte will
+ contain the Report number if the device uses numbered reports.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param data A buffer to put the read data into.
+ @param length The number of bytes to read. For devices with
+ multiple reports, make sure to read an extra byte for
+ the report number.
+
+ @returns
+ This function returns the actual number of bytes read and
+ -1 on error. If no packet was available to be read and
+ the handle is in non-blocking mode, this function returns 0.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
+
+ /** @brief Set the device handle to be non-blocking.
+
+ In non-blocking mode calls to hid_read() will return
+ immediately with a value of 0 if there is no data to be
+ read. In blocking mode, hid_read() will wait (block) until
+ there is data to read before returning.
+
+ Nonblocking can be turned on and off at any time.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param nonblock enable or not the nonblocking reads
+ - 1 to enable nonblocking
+ - 0 to disable nonblocking.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
+
+ /** @brief Send a Feature report to the device.
+
+ Feature reports are sent over the Control endpoint as a
+ Set_Report transfer. The first byte of @p data[] must
+ contain the Report ID. For devices which only support a
+ single report, this must be set to 0x0. The remaining bytes
+ contain the report data. Since the Report ID is mandatory,
+ calls to hid_send_feature_report() will always contain one
+ more byte than the report contains. For example, if a hid
+ report is 16 bytes long, 17 bytes must be passed to
+ hid_send_feature_report(): the Report ID (or 0x0, for
+ devices which do not use numbered reports), followed by the
+ report data (16 bytes). In this example, the length passed
+ in would be 17.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param data The data to send, including the report number as
+ the first byte.
+ @param length The length in bytes of the data to send, including
+ the report number.
+
+ @returns
+ This function returns the actual number of bytes written and
+ -1 on error.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
+
+ /** @brief Get a feature report from a HID device.
+
+ Set the first byte of @p data[] to the Report ID of the
+ report to be read. Make sure to allow space for this
+ extra byte in @p data[]. Upon return, the first byte will
+ still contain the Report ID, and the report data will
+ start in data[1].
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param data A buffer to put the read data into, including
+ the Report ID. Set the first byte of @p data[] to the
+ Report ID of the report to be read, or set it to zero
+ if your device does not use numbered reports.
+ @param length The number of bytes to read, including an
+ extra byte for the report ID. The buffer can be longer
+ than the actual report.
+
+ @returns
+ This function returns the number of bytes read plus
+ one for the report ID (which is still in the first
+ byte), or -1 on error.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
+
+ /** @brief Close a HID device.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ */
+ void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
+
+ /** @brief Get The Manufacturer String from a HID device.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param string A wide string buffer to put the data into.
+ @param maxlen The length of the buffer in multiples of wchar_t.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
+
+ /** @brief Get The Product String from a HID device.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param string A wide string buffer to put the data into.
+ @param maxlen The length of the buffer in multiples of wchar_t.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
+
+ /** @brief Get The Serial Number String from a HID device.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param string A wide string buffer to put the data into.
+ @param maxlen The length of the buffer in multiples of wchar_t.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
+
+ /** @brief Get a string from a HID device, based on its string index.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+ @param string_index The index of the string to get.
+ @param string A wide string buffer to put the data into.
+ @param maxlen The length of the buffer in multiples of wchar_t.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
+
+ /** @brief Get a string describing the last error which occurred.
+
+ @ingroup API
+ @param device A device handle returned from hid_open().
+
+ @returns
+ This function returns a string containing the last error
+ which occurred or NULL if none has occurred.
+ */
+ HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/log.h b/include/log.h
index 8eda4fb..7c25918 100644
--- a/include/log.h
+++ b/include/log.h
@@ -6,6 +6,12 @@
namespace nitrokey {
namespace log {
+//for MSVC
+#ifdef ERROR
+#undef ERROR
+#endif
+
+
enum class Loglevel : int { DEBUG_L2, DEBUG, INFO, WARNING, ERROR };
class LogHandler {
@@ -28,7 +34,7 @@ class Log {
Log() : mp_loghandler(&stdlog_handler), m_loglevel(Loglevel::WARNING) {}
static Log &instance() {
- if (mp_instance == NULL) mp_instance = new Log;
+ if (mp_instance == nullptr) mp_instance = new Log;
return *mp_instance;
}
@@ -47,4 +53,11 @@ class Log {
}
}
+
+#ifdef NO_LOG
+#define LOG(string, level) while(false){}
+#else
+#define LOG(string, level) nitrokey::log::Log::instance()((string), (level))
+#endif
+
#endif
diff --git a/include/misc.h b/include/misc.h
index 5158de0..111d772 100644
--- a/include/misc.h
+++ b/include/misc.h
@@ -6,10 +6,21 @@
#include <string.h>
#include "log.h"
#include "LibraryException.h"
+#include <sstream>
+#include <iomanip>
+
namespace nitrokey {
namespace misc {
+ template<typename T>
+ std::string toHex(T value){
+ using namespace std;
+ std::ostringstream oss;
+ oss << std::hex << std::setw(sizeof(value)*2) << std::setfill('0') << value;
+ return oss.str();
+ }
+
template <typename T>
void strcpyT(T& dest, const char* src){
@@ -17,7 +28,7 @@ namespace misc {
// throw EmptySourceStringException(slot_number);
return;
const size_t s_dest = sizeof dest;
- nitrokey::log::Log::instance()(std::string("strcpyT sizes dest src ")
+ LOG(std::string("strcpyT sizes dest src ")
+std::to_string(s_dest)+ " "
+std::to_string(strlen(src))+ " "
,nitrokey::log::Loglevel::DEBUG);
@@ -27,7 +38,7 @@ namespace misc {
strncpy((char*) &dest, src, s_dest);
}
-
+#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
template <typename T>
typename T::CommandPayload get_payload(){
//Create, initialize and return by value command payload
@@ -44,7 +55,8 @@ typename T::CommandPayload get_payload(){
CMDTYPE::CommandTransaction::run(stick, p);
}
- std::string hexdump(const char *p, size_t size, bool print_header=true);
+ std::string hexdump(const char *p, size_t size, bool print_header=true, bool print_ascii=true,
+ bool print_empty=true);
uint32_t stm_crc32(const uint8_t *data, size_t size);
std::vector<uint8_t> hex_string_to_byte(const char* hexString);
}
diff --git a/include/stick10_commands.h b/include/stick10_commands.h
index f02fd70..a50d0ae 100644
--- a/include/stick10_commands.h
+++ b/include/stick10_commands.h
@@ -1,16 +1,21 @@
#ifndef STICK10_COMMANDS_H
#define STICK10_COMMANDS_H
+
#include <bitset>
#include <iomanip>
#include <string>
#include <sstream>
-#include "inttypes.h"
-#include "command.h"
+#include <stdint.h>
#include "device_proto.h"
+#include "command.h"
+
+#pragma pack (push,1)
namespace nitrokey {
namespace proto {
+
+
/*
* Stick10 protocol definition
*/
@@ -293,18 +298,44 @@ class ReadSlot : Command<CommandID::READ_SLOT> {
struct ResponsePayload {
uint8_t slot_name[15];
- uint8_t config;
- uint8_t token_id[13];
- uint64_t counter;
+ union{
+ uint8_t _slot_config;
+ struct{
+ bool use_8_digits : 1;
+ bool use_enter : 1;
+ bool use_tokenID : 1;
+ };
+ };
+ union{
+ uint8_t slot_token_id[13]; /** OATH Token Identifier */
+ struct{ /** @see https://openauthentication.org/token-specs/ */
+ uint8_t omp[2];
+ uint8_t tt[2];
+ uint8_t mui[8];
+ uint8_t keyboard_layout; //disabled feature in nitroapp as of 20160805
+ } slot_token_fields;
+ };
+ union{
+ uint64_t slot_counter;
+ uint8_t slot_counter_s[8];
+ } __packed;
bool isValid() const { return true; }
std::string dissect() const {
std::stringstream ss;
ss << "slot_name:\t" << slot_name << std::endl;
- ss << "config:\t" << config << std::endl;
- ss << "token_id:\t" << token_id << std::endl;
- ss << "counter:\t" << counter << std::endl;
+ ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
+ ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
+ ss << "\tuse_enter(1):\t" << use_enter << std::endl;
+ ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
+
+ ss << "slot_token_id:\t";
+ for (auto i : slot_token_id)
+ ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ;
+ ss << std::endl;
+ ss << "slot_counter:\t[" << (int)slot_counter << "]\t"
+ << ::nitrokey::misc::hexdump((const char *)(&slot_counter), sizeof slot_counter, false);
return ss.str();
}
} __packed;
@@ -317,7 +348,10 @@ class GetStatus : Command<CommandID::GET_STATUS> {
public:
struct ResponsePayload {
uint16_t firmware_version;
- uint8_t card_serial[4];
+ union{
+ uint8_t card_serial[4];
+ uint32_t card_serial_u32;
+ } __packed;
union {
uint8_t general_config[5];
struct{
@@ -326,13 +360,12 @@ class GetStatus : Command<CommandID::GET_STATUS> {
uint8_t scrolllock; /** same as numlock */
uint8_t enable_user_password;
uint8_t delete_user_password;
- };
- };
+ } __packed;
+ } __packed;
bool isValid() const { return enable_user_password!=delete_user_password; }
std::string get_card_serial_hex() const {
- return ::nitrokey::misc::hexdump((const char *)(card_serial),
- sizeof card_serial, false);
+ return nitrokey::misc::toHex(card_serial_u32);
}
std::string dissect() const {
@@ -341,6 +374,7 @@ class GetStatus : Command<CommandID::GET_STATUS> {
<< "[" << firmware_version << "]" << "\t"
<< ::nitrokey::misc::hexdump(
(const char *)(&firmware_version), sizeof firmware_version, false);
+ ss << "card_serial_u32:\t" << std::hex << card_serial_u32 << std::endl;
ss << "card_serial:\t"
<< ::nitrokey::misc::hexdump((const char *)(card_serial),
sizeof card_serial, false);
@@ -812,8 +846,11 @@ class BuildAESKey : Command<CommandID::NEW_AES_KEY> {
typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
CommandTransaction;
+
};
+
}
}
}
+#pragma pack (pop)
#endif
diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h
index 9594d1e..ead5add 100644
--- a/include/stick10_commands_0.8.h
+++ b/include/stick10_commands_0.8.h
@@ -9,7 +9,7 @@
#include <iomanip>
#include <string>
#include <sstream>
-#include "inttypes.h"
+#include <stdint.h>
#include "command.h"
#include "device_proto.h"
#include "stick10_commands.h"
diff --git a/include/stick20_commands.h b/include/stick20_commands.h
index e6df770..d88919b 100644
--- a/include/stick20_commands.h
+++ b/include/stick20_commands.h
@@ -1,12 +1,15 @@
#ifndef STICK20_COMMANDS_H
#define STICK20_COMMANDS_H
-#include "inttypes.h"
+
+
+#include <stdint.h>
#include "command.h"
#include <string>
#include <sstream>
#include "device_proto.h"
+#pragma pack (push,1)
namespace nitrokey {
namespace proto {
@@ -26,9 +29,12 @@ namespace nitrokey {
public PasswordCommand<CommandID::UNLOCK_USER_PASSWORD, PasswordKind::Admin> {};
class EnableEncryptedPartition : public PasswordCommand<CommandID::ENABLE_CRYPTED_PARI> {};
- class DisableEncryptedPartition : public PasswordCommand<CommandID::DISABLE_CRYPTED_PARI> {};
class EnableHiddenEncryptedPartition : public PasswordCommand<CommandID::ENABLE_HIDDEN_CRYPTED_PARI> {};
- class DisableHiddenEncryptedPartition : public PasswordCommand<CommandID::DISABLE_CRYPTED_PARI> {};
+
+ //FIXME the volume disabling commands do not need password
+ class DisableEncryptedPartition : public PasswordCommand<CommandID::DISABLE_CRYPTED_PARI> {};
+ class DisableHiddenEncryptedPartition : public PasswordCommand<CommandID::DISABLE_HIDDEN_CRYPTED_PARI> {};
+
class EnableFirmwareUpdate : public PasswordCommand<CommandID::ENABLE_FIRMWARE_UPDATE> {};
class ChangeUpdatePassword : Command<CommandID::CHANGE_UPDATE_PIN> {
@@ -123,6 +129,9 @@ namespace nitrokey {
StorageCommandResponsePayload::TransmissionData transmission_data;
uint16_t MagicNumber_StickConfig_u16;
+ /**
+ * READ_WRITE_ACTIVE = ReadWriteFlagUncryptedVolume_u8 == 0;
+ */
uint8_t ReadWriteFlagUncryptedVolume_u8;
uint8_t ReadWriteFlagCryptedVolume_u8;
@@ -130,18 +139,36 @@ namespace nitrokey {
uint8_t VersionInfo_au8[4];
struct {
uint8_t __unused;
- uint8_t major;
- uint8_t __unused2;
uint8_t minor;
+ uint8_t __unused2;
+ uint8_t major;
} __packed versionInfo;
- };
+ } __packed;
uint8_t ReadWriteFlagHiddenVolume_u8;
uint8_t FirmwareLocked_u8;
- uint8_t NewSDCardFound_u8;
+
+ union{
+ uint8_t NewSDCardFound_u8;
+ struct {
+ bool NewCard :1;
+ uint8_t Counter :7;
+ } __packed NewSDCardFound_st;
+ } __packed;
+
+ /**
+ * SD card FILLED with random chars
+ */
uint8_t SDFillWithRandomChars_u8;
uint32_t ActiveSD_CardID_u32;
- uint8_t VolumeActiceFlag_u8;
+ union{
+ uint8_t VolumeActiceFlag_u8;
+ struct {
+ bool unencrypted :1;
+ bool encrypted :1;
+ bool hidden :1;
+ } __packed VolumeActiceFlag_st;
+ } __packed;
uint8_t NewSmartCardFound_u8;
uint8_t UserPwRetryCount;
uint8_t AdminPwRetryCount;
@@ -308,10 +335,12 @@ namespace nitrokey {
typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload>
CommandTransaction;
};
+
}
}
}
#undef print_to_ss
+#pragma pack (pop)
#endif
diff --git a/log.cc b/log.cc
index 5889a1e..70853fc 100644
--- a/log.cc
+++ b/log.cc
@@ -7,7 +7,7 @@
namespace nitrokey {
namespace log {
-Log *Log::mp_instance = NULL;
+Log *Log::mp_instance = nullptr;
StdlogHandler stdlog_handler;
std::string LogHandler::loglevel_to_str(Loglevel lvl) {
@@ -27,7 +27,7 @@ std::string LogHandler::loglevel_to_str(Loglevel lvl) {
}
void Log::operator()(const std::string &logstr, Loglevel lvl) {
- if (mp_loghandler != NULL)
+ if (mp_loghandler != nullptr)
if ((int)(lvl) >= (int)(m_loglevel)) mp_loghandler->print(logstr, lvl);
}
diff --git a/misc.cc b/misc.cc
index 3f15520..5eb81b5 100644
--- a/misc.cc
+++ b/misc.cc
@@ -5,18 +5,21 @@
#include <cstdlib>
#include <cstring>
#include "LibraryException.h"
+#include <vector>
namespace nitrokey {
namespace misc {
-std::vector<uint8_t> hex_string_to_byte(const char* hexString){
+
+
+::std::vector<uint8_t> hex_string_to_byte(const char* hexString){
const size_t big_string_size = 256; //arbitrary 'big' number
const size_t s_size = strlen(hexString);
const size_t d_size = s_size/2;
if (s_size%2!=0 || s_size>big_string_size){
throw InvalidHexString(0);
}
- auto data = std::vector<uint8_t>();
+ auto data = ::std::vector<uint8_t>();
data.reserve(d_size);
char buf[2];
@@ -36,8 +39,9 @@ std::vector<uint8_t> hex_string_to_byte(const char* hexString){
};
#include <cctype>
-std::string hexdump(const char *p, size_t size, bool print_header) {
- std::stringstream out;
+::std::string hexdump(const char *p, size_t size, bool print_header,
+ bool print_ascii, bool print_empty) {
+ ::std::stringstream out;
char formatbuf[128];
const char *pstart = p;
@@ -53,19 +57,21 @@ std::string hexdump(const char *p, size_t size, bool print_header) {
snprintf(formatbuf, 128, "%02x ", uint8_t(*p));
out << formatbuf;
} else {
- out << "-- ";
+ if(print_empty)
+ out << "-- ";
}
}
- out << "\t";
-
- for (const char *le = pp + 16; pp < le && pp < pend; pp++) {
- if (std::isgraph(*pp))
- out << uint8_t(*pp);
- else
- out << '.';
- }
- out << std::endl;
+ if(print_ascii){
+ out << "\t";
+ for (const char *le = pp + 16; pp < le && pp < pend; pp++) {
+ if (std::isgraph(*pp))
+ out << uint8_t(*pp);
+ else
+ out << '.';
+ }
+ }
+ out << ::std::endl;
}
return out.str();
}
diff --git a/python_bindings/pybind11 b/python_bindings/pybind11
deleted file mode 160000
-Subproject 1f66a584278dfd1ad88be19d5e4996302793a19
diff --git a/unittest/Makefile b/unittest/Makefile
deleted file mode 100644
index dbd003e..0000000
--- a/unittest/Makefile
+++ /dev/null
@@ -1,33 +0,0 @@
-CC = $(PREFIX)-gcc
-#CXX = $(PREFIX)-g++
-CXX = clang++-3.8
-LD = $(CXX)
-
-INCLUDE = -I../include -ICatch/single_include/
-LIB = -L../build
-LDLIBS = -lnitrokey
-BUILD = build
-
-CXXFLAGS = -std=c++14 -fPIC -Wno-gnu-variable-sized-type-not-at-end
-
-CXXSOURCES = $(wildcard *.cc)
-TARGETS = $(CXXSOURCES:%.cc=$(BUILD)/%)
-DEPENDS = $(CXXSOURCES:%.cc=$(BUILD)/%.d)
-
-$(BUILD)/%.d: %.cc
- $(CXX) -M $< -o $@ $(INCLUDE) $(CXXFLAGS)
-
-$(BUILD)/%: %.cc $(DEPENDS)
- $(CXX) $< -o $@ $(INCLUDE) $(LIB) $(CXXFLAGS) $(LDLIBS)
-
-all: $(TARGETS)
-
-clean:
- rm -f $(TARGETS)
-
-mrproper: clean
- rm -f $(BUILD)/*.d
-
-.PHONY: all clean mrproper
-
-include $(wildcard build/*.d)
diff --git a/unittest/conftest.py b/unittest/conftest.py
index 88bf7d0..67b45aa 100644
--- a/unittest/conftest.py
+++ b/unittest/conftest.py
@@ -22,14 +22,30 @@ def C(request):
a = iter(declarations)
for declaration in a:
- if declaration.startswith('extern') and not '"C"' in declaration:
- declaration = declaration.replace('extern', '').strip()
+ if declaration.startswith('NK_C_API'):
+ declaration = declaration.replace('NK_C_API', '').strip()
while not ';' in declaration:
declaration += (next(a)).strip()
print(declaration)
ffi.cdef(declaration, override=True)
- C = ffi.dlopen("../build/libnitrokey.so")
+ C = None
+ import os, sys
+ path_build = os.path.join("..", "build")
+ paths = [ os.path.join(path_build,"libnitrokey-log.so"),
+ os.path.join(path_build,"libnitrokey.so")]
+ for p in paths:
+ print p
+ if os.path.exists(p):
+ C = ffi.dlopen(p)
+ break
+ else:
+ print("File does not exist: " + p)
+ print("Trying another")
+ if not C:
+ print("No library file found")
+ sys.exit(1)
+
C.NK_set_debug(False)
nk_login = C.NK_login_auto()
if nk_login != 1:
diff --git a/unittest/requirements.txt b/unittest/requirements.txt
index 7224741..2cb9c05 100644
--- a/unittest/requirements.txt
+++ b/unittest/requirements.txt
@@ -2,3 +2,4 @@ cffi
pytest-repeat
pytest-randomly
enum
+oath \ No newline at end of file
diff --git a/unittest/setup_python_dependencies.sh b/unittest/setup_python_dependencies.sh
new file mode 100644
index 0000000..0f1a0f7
--- /dev/null
+++ b/unittest/setup_python_dependencies.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+pip install -r requirements.txt --user
diff --git a/unittest/test.cc b/unittest/test.cc
index 6744b45..15235bd 100644
--- a/unittest/test.cc
+++ b/unittest/test.cc
@@ -1,4 +1,3 @@
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
#include "catch.hpp"
#include <iostream>
@@ -13,8 +12,9 @@ using namespace nitrokey::proto::stick10;
using namespace nitrokey::log;
using namespace nitrokey::misc;
+using Dev10 = std::shared_ptr<Stick10>;
-std::string getSlotName(Stick10 &stick, int slotNo) {
+std::string getSlotName(Dev10 stick, int slotNo) {
auto slot_req = get_payload<ReadSlot>();
slot_req.slot_number = slotNo;
auto slot = ReadSlot::CommandTransaction::run(stick, slot_req);
@@ -23,8 +23,8 @@ std::string getSlotName(Stick10 &stick, int slotNo) {
}
TEST_CASE("Slot names are correct", "[slotNames]") {
- Stick10 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick10>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
@@ -79,5 +79,5 @@ TEST_CASE("Slot names are correct", "[slotNames]") {
REQUIRE(sName == std::string("login1"));
}
- stick.disconnect();
+ stick->disconnect();
}
diff --git a/unittest/test2.cc b/unittest/test2.cc
index 00e70e3..31dbce8 100644
--- a/unittest/test2.cc
+++ b/unittest/test2.cc
@@ -1,4 +1,3 @@
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
static const char *const default_admin_pin = "12345678";
static const char *const default_user_pin = "123456";
@@ -20,9 +19,10 @@ using namespace nitrokey::proto::stick20;
using namespace nitrokey::log;
using namespace nitrokey::misc;
+#include <memory>
template<typename CMDTYPE>
-void execute_password_command(Device &stick, const char *password, const char kind = 'P') {
+void execute_password_command(std::shared_ptr<Device> stick, const char *password, const char kind = 'P') {
auto p = get_payload<CMDTYPE>();
if (kind == 'P'){
p.set_kind_user();
@@ -47,8 +47,8 @@ void SKIP_TEST() {
TEST_CASE("long operation test", "[test_long]") {
SKIP_TEST();
- Stick20 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick20>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
try{
@@ -123,8 +123,8 @@ TEST_CASE("test device commands ids", "[fast]") {
}
TEST_CASE("test device internal status with various commands", "[fast]") {
- Stick20 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick20>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
@@ -147,8 +147,8 @@ TEST_CASE("test device internal status with various commands", "[fast]") {
}
TEST_CASE("setup hidden volume test", "[hidden]") {
- Stick20 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick20>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
stick10::LockDevice::CommandTransaction::run(stick);
@@ -170,8 +170,8 @@ TEST_CASE("setup hidden volume test", "[hidden]") {
}
TEST_CASE("setup multiple hidden volumes", "[hidden]") {
- Stick20 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick20>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
@@ -207,8 +207,8 @@ TEST_CASE("setup multiple hidden volumes", "[hidden]") {
TEST_CASE("update password change", "[dangerous]") {
SKIP_TEST();
- Stick20 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick20>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
@@ -228,8 +228,8 @@ TEST_CASE("update password change", "[dangerous]") {
}
TEST_CASE("general test", "[test]") {
- Stick20 stick;
- bool connected = stick.connect();
+ auto stick = make_shared<Stick20>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
diff --git a/unittest/test3.cc b/unittest/test3.cc
index 9049365..9e0ef11 100644
--- a/unittest/test3.cc
+++ b/unittest/test3.cc
@@ -1,8 +1,3 @@
-//
-// Created by sz on 08.11.16.
-//
-
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
static const char *const default_admin_pin = "12345678";
static const char *const default_user_pin = "123456";
@@ -26,13 +21,16 @@ using namespace nitrokey::proto::stick10_08;
using namespace nitrokey::log;
using namespace nitrokey::misc;
-void connect_and_setup(Stick10 &stick) {
- bool connected = stick.connect();
+using Dev = Stick10;
+using Dev10 = std::shared_ptr<Dev>;
+
+void connect_and_setup(Dev10 stick) {
+ bool connected = stick->connect();
REQUIRE(connected == true);
Log::instance().set_loglevel(Loglevel::DEBUG);
}
-void authorize(Stick10 &stick) {
+void authorize(Dev10 stick) {
auto authreq = get_payload<FirstAuthenticate>();
strcpy((char *) (authreq.card_password), default_admin_pin);
strcpy((char *) (authreq.temporary_password), temporary_password);
@@ -45,7 +43,8 @@ void authorize(Stick10 &stick) {
}
TEST_CASE("write slot", "[pronew]"){
- Stick10 stick;
+ auto stick = make_shared<Dev>();
+
connect_and_setup(stick);
authorize(stick);
@@ -81,7 +80,7 @@ TEST_CASE("write slot", "[pronew]"){
TEST_CASE("erase slot", "[pronew]"){
- Stick10 stick;
+ auto stick = make_shared<Dev>();
connect_and_setup(stick);
authorize(stick);
@@ -107,7 +106,7 @@ TEST_CASE("erase slot", "[pronew]"){
}
TEST_CASE("write general config", "[pronew]") {
- Stick10 stick;
+ auto stick = make_shared<Dev>();
connect_and_setup(stick);
authorize(stick);
@@ -121,7 +120,7 @@ TEST_CASE("write general config", "[pronew]") {
}
TEST_CASE("authorize user HOTP", "[pronew]") {
- Stick10 stick;
+ auto stick = make_shared<Dev>();
connect_and_setup(stick);
authorize(stick);
@@ -164,7 +163,7 @@ TEST_CASE("authorize user HOTP", "[pronew]") {
}
TEST_CASE("check firmware version", "[pronew]") {
- Stick10 stick;
+ auto stick = make_shared<Dev>();
connect_and_setup(stick);
auto p = GetStatus::CommandTransaction::run(stick);
@@ -172,7 +171,7 @@ TEST_CASE("check firmware version", "[pronew]") {
}
TEST_CASE("authorize user TOTP", "[pronew]") {
- Stick10 stick;
+ auto stick = make_shared<Dev>();
connect_and_setup(stick);
authorize(stick);
diff --git a/unittest/test_C_API.cpp b/unittest/test_C_API.cpp
index 37d3c7f..160145b 100644
--- a/unittest/test_C_API.cpp
+++ b/unittest/test_C_API.cpp
@@ -24,8 +24,8 @@ TEST_CASE("Check retry count", "[BASIC]") {
}
TEST_CASE("Check long strings", "[STANDARD]") {
- char* longPin = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
- char *pin = "123123123";
+ const char* longPin = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ const char* pin = "123123123";
auto result = NK_change_user_PIN(longPin, pin);
REQUIRE(result == TOO_LONG_STRING);
result = NK_change_user_PIN(pin, longPin);
diff --git a/unittest/test_HOTP.cc b/unittest/test_HOTP.cc
index d31df55..e6f7d7c 100644
--- a/unittest/test_HOTP.cc
+++ b/unittest/test_HOTP.cc
@@ -1,4 +1,3 @@
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
#include "catch.hpp"
#include <iostream>
#include "device_proto.h"
@@ -14,7 +13,7 @@ using namespace nitrokey::log;
using namespace nitrokey::misc;
void hexStringToByte(uint8_t data[], const char* hexString){
- assert(strlen(hexString)%2==0);
+ REQUIRE(strlen(hexString)%2==0);
char buf[2];
for(int i=0; i<strlen(hexString); i++){
buf[i%2] = hexString[i];
@@ -34,8 +33,8 @@ TEST_CASE("test secret", "[functions]") {
}
TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") {
- Stick10 stick;
- bool connected = stick.connect();
+ std::shared_ptr<Stick10> stick = make_shared<Stick10>();
+ bool connected = stick->connect();
REQUIRE(connected == true);
@@ -98,5 +97,5 @@ TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") {
}
- stick.disconnect();
+ stick->disconnect();
}
diff --git a/unittest/test_pro.py b/unittest/test_pro.py
index 4a2a504..0140994 100644
--- a/unittest/test_pro.py
+++ b/unittest/test_pro.py
@@ -22,6 +22,72 @@ def test_write_password_safe_slot(C):
assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_OK
+@pytest.mark.slowtest
+def test_write_all_password_safe_slots_and_read_10_times(C):
+ def fill(s, wid):
+ assert wid >= len(s)
+ numbers = '1234567890'*4
+ s += numbers[:wid-len(s)]
+ assert len(s) == wid
+ return s
+
+ def get_pass(suffix):
+ return fill('pass' + suffix, 20)
+
+ def get_loginname(suffix):
+ return fill('login' + suffix, 32)
+
+ def get_slotname(suffix):
+ return fill('slotname' + suffix, 11)
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ PWS_slot_count = 16
+ for i in range(0, PWS_slot_count):
+ iss = str(i)
+ assert C.NK_write_password_safe_slot(i,
+ get_slotname(iss), get_loginname(iss),
+ get_pass(iss)) == DeviceErrorCode.STATUS_OK
+
+ for j in range(0, 10):
+ for i in range(0, PWS_slot_count):
+ iss = str(i)
+ assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss)
+ assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss)
+ assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss)
+
+
+@pytest.mark.slowtest
+@pytest.mark.xfail(reason="This test should be run directly after test_write_all_password_safe_slots_and_read_10_times")
+def test_read_all_password_safe_slots_10_times(C):
+ def fill(s, wid):
+ assert wid >= len(s)
+ numbers = '1234567890'*4
+ s += numbers[:wid-len(s)]
+ assert len(s) == wid
+ return s
+
+ def get_pass(suffix):
+ return fill('pass' + suffix, 20)
+
+ def get_loginname(suffix):
+ return fill('login' + suffix, 32)
+
+ def get_slotname(suffix):
+ return fill('slotname' + suffix, 11)
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ PWS_slot_count = 16
+
+ for j in range(0, 10):
+ for i in range(0, PWS_slot_count):
+ iss = str(i)
+ assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss)
+ assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss)
+ assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss)
+
+
def test_get_password_safe_slot_name(C):
assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
assert C.NK_write_password_safe_slot(0, 'slotname1', 'login1', 'pass1') == DeviceErrorCode.STATUS_OK
@@ -82,6 +148,9 @@ def test_regenerate_aes_key(C):
def test_enable_password_safe_after_factory_reset(C):
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ if is_storage(C):
+ # for some reason storage likes to be authenticated before reset (to investigate)
+ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
assert C.NK_factory_reset(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
wait(10)
if is_storage(C):
@@ -523,7 +592,7 @@ def test_OTP_secret_started_from_null(C, secret):
skip_if_device_version_lower_than({'S': 43, 'P': 8})
if len(secret) > 40:
# feature: 320 bit long secret handling
- skip_if_device_version_lower_than({'S': 44, 'P': 8})
+ skip_if_device_version_lower_than({'P': 8})
oath = pytest.importorskip("oath")
lib_at = lambda t: oath.hotp(secret, t, format='dec6')
@@ -617,8 +686,9 @@ def test_TOTP_secrets(C, secret):
'''
skip_if_device_version_lower_than({'S': 44, 'P': 8})
- if is_pro_rtm_07(C) and len(secret)>20*2: #*2 since secret is in hex
- pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 ")
+ if len(secret)>20*2: #*2 since secret is in hex
+ # pytest.skip("Secret lengths over 20 bytes are not supported by NK Pro 0.7 and NK Storage")
+ skip_if_device_version_lower_than({'P': 8})
slot_number = 0
time = 0
period = 30
@@ -645,10 +715,11 @@ def test_TOTP_secrets(C, secret):
@pytest.mark.parametrize("secret", [RFC_SECRET, 2*RFC_SECRET, '12'*10, '12'*30] )
def test_HOTP_secrets(C, secret):
"""
- NK Pro 0.8+, NK Storage 0.44+
+ NK Pro 0.8+
feature needed: support for 320bit secrets
"""
- skip_if_device_version_lower_than({'S': 44, 'P': 8})
+ if len(secret)>40:
+ skip_if_device_version_lower_than({'P': 8})
slot_number = 0
counter = 0
@@ -695,7 +766,7 @@ def test_edit_OTP_slot(C):
"""
should change slots counter and name without changing its secret (using null secret for second update)
"""
- # counter does not reset under Storage v0.43
+ # counter is not getting updated under Storage v0.43 - #TOREPORT
skip_if_device_version_lower_than({'S': 44, 'P': 7})
secret = RFC_SECRET
diff --git a/unittest/test_storage.py b/unittest/test_storage.py
index a1c59aa..da7c9a3 100644
--- a/unittest/test_storage.py
+++ b/unittest/test_storage.py
@@ -57,10 +57,11 @@ def test_encrypted_volume_unlock_hidden(C):
assert C.NK_create_hidden_volume(0, 20, 21, hidden_volume_password) == DeviceErrorCode.STATUS_OK
assert C.NK_unlock_hidden_volume(hidden_volume_password) == DeviceErrorCode.STATUS_OK
-@pytest.mark.skip(reason='hangs device, to report')
-def test_encrypted_volume_setup_multiple_hidden(C):
- skip_if_device_version_lower_than({'S': 43})
- hidden_volume_password = 'hiddenpassword'
+
+def test_encrypted_volume_setup_multiple_hidden_lock(C):
+ import random
+ skip_if_device_version_lower_than({'S': 45}) #hangs device on lower version
+ hidden_volume_password = 'hiddenpassword' + str(random.randint(0,100))
p = lambda i: hidden_volume_password + str(i)
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
@@ -72,6 +73,149 @@ def test_encrypted_volume_setup_multiple_hidden(C):
assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+@pytest.mark.parametrize("volumes_to_setup", range(1, 5))
+def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes(C, volumes_to_setup):
+ skip_if_device_version_lower_than({'S': 43})
+ hidden_volume_password = 'hiddenpassword'
+ p = lambda i: hidden_volume_password + str(i)
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ for i in range(volumes_to_setup):
+ assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+ for i in range(volumes_to_setup):
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ # TODO mount and test for files
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+
+@pytest.mark.parametrize("volumes_to_setup", range(1, 5))
+def test_encrypted_volume_setup_multiple_hidden_no_lock_device_volumes_unlock_at_once(C, volumes_to_setup):
+ skip_if_device_version_lower_than({'S': 43})
+ hidden_volume_password = 'hiddenpassword'
+ p = lambda i: hidden_volume_password + str(i)
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ for i in range(volumes_to_setup):
+ assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+ for i in range(volumes_to_setup):
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ # TODO mount and test for files
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+
+@pytest.mark.parametrize("use_slot", range(4))
+def test_encrypted_volume_setup_one_hidden_no_lock_device_slot(C, use_slot):
+ skip_if_device_version_lower_than({'S': 43})
+ hidden_volume_password = 'hiddenpassword'
+ p = lambda i: hidden_volume_password + str(i)
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ i = use_slot
+ assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+ for j in range(3):
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ # TODO mount and test for files
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+
+def test_password_safe_slot_name_corruption(C):
+ skip_if_device_version_lower_than({'S': 43})
+ volumes_to_setup = 4
+ # connected with encrypted volumes, possible also with hidden
+ def fill(s, wid):
+ assert wid >= len(s)
+ numbers = '1234567890' * 4
+ s += numbers[:wid - len(s)]
+ assert len(s) == wid
+ return s
+
+ def get_pass(suffix):
+ return fill('pass' + suffix, 20)
+
+ def get_loginname(suffix):
+ return fill('login' + suffix, 32)
+
+ def get_slotname(suffix):
+ return fill('slotname' + suffix, 11)
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_enable_password_safe(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ PWS_slot_count = 16
+ for i in range(0, PWS_slot_count):
+ iss = str(i)
+ assert C.NK_write_password_safe_slot(i,
+ get_slotname(iss), get_loginname(iss),
+ get_pass(iss)) == DeviceErrorCode.STATUS_OK
+
+ def check_PWS_correctness(C):
+ for i in range(0, PWS_slot_count):
+ iss = str(i)
+ assert gs(C.NK_get_password_safe_slot_name(i)) == get_slotname(iss)
+ assert gs(C.NK_get_password_safe_slot_login(i)) == get_loginname(iss)
+ assert gs(C.NK_get_password_safe_slot_password(i)) == get_pass(iss)
+
+ hidden_volume_password = 'hiddenpassword'
+ p = lambda i: hidden_volume_password + str(i)
+ def check_volumes_correctness(C):
+ for i in range(volumes_to_setup):
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ # TODO mount and test for files
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+ check_PWS_correctness(C)
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ for i in range(volumes_to_setup):
+ assert C.NK_create_hidden_volume(i, 20+i*10, 20+i*10+i+1, p(i)) == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+
+ check_volumes_correctness(C)
+ check_PWS_correctness(C)
+ check_volumes_correctness(C)
+ check_PWS_correctness(C)
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ check_volumes_correctness(C)
+ check_PWS_correctness(C)
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ check_volumes_correctness(C)
+ check_PWS_correctness(C)
+
+def test_hidden_volume_corruption(C):
+ # bug: this should return error without unlocking encrypted volume each hidden volume lock, but it does not
+ assert C.NK_lock_encrypted_volume() == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ hidden_volume_password = 'hiddenpassword'
+ p = lambda i: hidden_volume_password + str(i)
+ for i in range(4):
+ assert C.NK_unlock_encrypted_volume(DefaultPasswords.USER) == DeviceErrorCode.STATUS_OK
+ assert C.NK_unlock_hidden_volume(p(i)) == DeviceErrorCode.STATUS_OK
+ wait(2)
+ assert C.NK_lock_hidden_volume() == DeviceErrorCode.STATUS_OK
+
def test_unencrypted_volume_set_read_only(C):
skip_if_device_version_lower_than({'S': 43})
assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
@@ -94,7 +238,8 @@ def test_clear_new_sd_card_notification(C):
assert C.NK_clear_new_sd_card_warning(DefaultPasswords.ADMIN) == DeviceErrorCode.STATUS_OK
-@pytest.mark.skip
+@pytest.mark.slowtest
+@pytest.mark.skip(reason='long test (about 1h)')
def test_fill_SD_card(C):
skip_if_device_version_lower_than({'S': 43})
status = C.NK_fill_SD_card_with_random_data(DefaultPasswords.ADMIN)