aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt9
-rw-r--r--NK_C_API.cc101
-rw-r--r--NK_C_API.h118
-rw-r--r--NitrokeyManager.cc6
-rw-r--r--libnitrokey/NitrokeyManager.h12
-rw-r--r--libnitrokey/version.h33
-rw-r--r--unittest/conftest.py2
-rw-r--r--unittest/test_offline.cc10
-rw-r--r--unittest/test_pro.py49
-rw-r--r--version.cc.in37
10 files changed, 340 insertions, 37 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3dacb48..06ab448 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,7 +66,9 @@ set(SOURCE_FILES
NitrokeyManager.cc
NK_C_API.h
NK_C_API.cc
- DeviceCommunicationExceptions.cpp)
+ DeviceCommunicationExceptions.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/version.cc
+ )
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build all libraries as shared")
add_library(nitrokey ${SOURCE_FILES})
@@ -115,6 +117,11 @@ IF (LOG_VOLATILE_DATA)
ENDIF()
+# generate version.h
+exec_program("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe --always" OUTPUT_VARIABLE PROJECT_VERSION_GIT)
+configure_file("version.cc.in" "version.cc" @ONLY)
+
+
file(GLOB LIB_INCLUDES "libnitrokey/*.h" "NK_C_API.h")
install (FILES ${LIB_INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
install (TARGETS nitrokey DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/NK_C_API.cc b/NK_C_API.cc
index 8e005b8..01963fc 100644
--- a/NK_C_API.cc
+++ b/NK_C_API.cc
@@ -21,10 +21,13 @@
#include "NK_C_API.h"
#include <iostream>
+#include <tuple>
#include "libnitrokey/NitrokeyManager.h"
#include <cstring>
#include "libnitrokey/LibraryException.h"
#include "libnitrokey/cxx_semantics.h"
+#include "libnitrokey/stick20_commands.h"
+#include "version.h"
#ifdef _MSC_VER
#ifdef _WIN32
@@ -52,11 +55,11 @@ T* duplicate_vector_and_clear(std::vector<T> &v){
return d;
}
-template <typename T>
-uint8_t * get_with_array_result(T func){
+template <typename R, typename T>
+std::tuple<int, R> get_with_status(T func, R fallback) {
NK_last_command_status = 0;
try {
- return func();
+ return std::make_tuple(0, func());
}
catch (CommandFailedException & commandFailedException){
NK_last_command_status = commandFailedException.last_command_status;
@@ -67,43 +70,26 @@ uint8_t * get_with_array_result(T func){
catch (const DeviceCommunicationException &deviceException){
NK_last_command_status = 256-deviceException.getType();
}
- return nullptr;
+ return std::make_tuple(NK_last_command_status, fallback);
+}
+
+template <typename T>
+uint8_t * get_with_array_result(T func){
+ return std::get<1>(get_with_status<uint8_t*>(func, nullptr));
}
template <typename T>
char* get_with_string_result(T func){
- NK_last_command_status = 0;
- try {
- return func();
- }
- catch (CommandFailedException & commandFailedException){
- NK_last_command_status = commandFailedException.last_command_status;
+ auto result = std::get<1>(get_with_status<char*>(func, nullptr));
+ if (result == nullptr) {
+ return strndup("", MAXIMUM_STR_REPLY_LENGTH);
}
- catch (LibraryException & libraryException){
- NK_last_command_status = libraryException.exception_id();
- }
- catch (const DeviceCommunicationException &deviceException){
- NK_last_command_status = 256-deviceException.getType();
- }
- return strndup("", MAXIMUM_STR_REPLY_LENGTH);
+ return result;
}
template <typename T>
auto get_with_result(T func){
- NK_last_command_status = 0;
- try {
- return func();
- }
- catch (CommandFailedException & commandFailedException){
- NK_last_command_status = commandFailedException.last_command_status;
- }
- catch (LibraryException & libraryException){
- NK_last_command_status = libraryException.exception_id();
- }
- catch (const DeviceCommunicationException &deviceException){
- NK_last_command_status = 256-deviceException.getType();
- }
- return static_cast<decltype(func())>(0);
+ return std::get<1>(get_with_status(func, static_cast<decltype(func())>(0)));
}
template <typename T>
@@ -353,6 +339,18 @@ extern "C" {
m->set_loglevel(level);
}
+ NK_C_API unsigned int NK_get_major_library_version() {
+ return get_major_library_version();
+ }
+
+ NK_C_API unsigned int NK_get_minor_library_version() {
+ return get_minor_library_version();
+ }
+
+ NK_C_API const char* NK_get_library_version() {
+ return get_library_version();
+ }
+
NK_C_API int NK_totp_set_time(uint64_t time) {
auto m = NitrokeyManager::instance();
return get_without_result([&]() {
@@ -360,11 +358,15 @@ extern "C" {
});
}
- NK_C_API int NK_totp_get_time() {
+ NK_C_API int NK_totp_set_time_soft(uint64_t time) {
auto m = NitrokeyManager::instance();
return get_without_result([&]() {
- m->get_time(0); // FIXME check how that should work
+ m->set_time_soft(time);
});
+ }
+
+ NK_C_API int NK_totp_get_time() {
+ return 0;
}
NK_C_API int NK_change_admin_PIN(const char *current_PIN, const char *new_PIN) {
@@ -596,6 +598,39 @@ extern "C" {
});
}
+ NK_C_API int NK_get_status_storage(NK_storage_status* out) {
+ if (out == nullptr) {
+ return -1;
+ }
+ auto m = NitrokeyManager::instance();
+ auto result = get_with_status([&]() {
+ return m->get_status_storage();
+ }, proto::stick20::DeviceConfigurationResponsePacket::ResponsePayload());
+ auto error_code = std::get<0>(result);
+ if (error_code != 0) {
+ return error_code;
+ }
+
+ auto status = std::get<1>(result);
+ out->unencrypted_volume_read_only = status.ReadWriteFlagUncryptedVolume_u8 != 0;
+ out->unencrypted_volume_active = status.VolumeActiceFlag_st.unencrypted;
+ out->encrypted_volume_read_only = status.ReadWriteFlagCryptedVolume_u8 != 0;
+ out->encrypted_volume_active = status.VolumeActiceFlag_st.encrypted;
+ out->hidden_volume_read_only = status.ReadWriteFlagHiddenVolume_u8 != 0;
+ out->hidden_volume_active = status.VolumeActiceFlag_st.hidden;
+ out->firmware_version_major = status.versionInfo.major;
+ out->firmware_version_minor = status.versionInfo.minor;
+ out->firmware_locked = status.FirmwareLocked_u8 != 0;
+ out->serial_number_sd_card = status.ActiveSD_CardID_u32;
+ out->serial_number_smart_card = status.ActiveSmartCardID_u32;
+ out->user_retry_count = status.UserPwRetryCount;
+ out->admin_retry_count = status.AdminPwRetryCount;
+ out->new_sd_card_found = status.NewSDCardFound_st.NewCard;
+ out->filled_with_random = (status.SDFillWithRandomChars_u8 & 0x01) != 0;
+ out->stick_initialized = status.StickKeysNotInitiated == 0;
+ return 0;
+ }
+
NK_C_API char* NK_get_SD_usage_data_as_string() {
auto m = NitrokeyManager::instance();
return get_with_string_result([&]() {
diff --git a/NK_C_API.h b/NK_C_API.h
index 5985c0b..f23bb5d 100644
--- a/NK_C_API.h
+++ b/NK_C_API.h
@@ -52,6 +52,77 @@ extern "C" {
};
/**
+ * Stores the status of a Storage device.
+ */
+ struct NK_storage_status {
+ /**
+ * Indicates whether the unencrypted volume is read-only.
+ */
+ bool unencrypted_volume_read_only;
+ /**
+ * Indicates whether the unencrypted volume is active.
+ */
+ bool unencrypted_volume_active;
+ /**
+ * Indicates whether the encrypted volume is read-only.
+ */
+ bool encrypted_volume_read_only;
+ /**
+ * Indicates whether the encrypted volume is active.
+ */
+ bool encrypted_volume_active;
+ /**
+ * Indicates whether the hidden volume is read-only.
+ */
+ bool hidden_volume_read_only;
+ /**
+ * Indicates whether the hidden volume is active.
+ */
+ bool hidden_volume_active;
+ /**
+ * The major firmware version, e. g. 0 in v0.40.
+ */
+ uint8_t firmware_version_major;
+ /**
+ * The minor firmware version, e. g. 40 in v0.40.
+ */
+ uint8_t firmware_version_minor;
+ /**
+ * Indicates whether the firmware is locked.
+ */
+ bool firmware_locked;
+ /**
+ * The serial number of the SD card in the Storage stick.
+ */
+ uint32_t serial_number_sd_card;
+ /**
+ * The serial number of the smart card in the Storage stick.
+ */
+ uint32_t serial_number_smart_card;
+ /**
+ * The number of remaining login attempts for the user PIN.
+ */
+ uint8_t user_retry_count;
+ /**
+ * The number of remaining login attempts for the admin PIN.
+ */
+ uint8_t admin_retry_count;
+ /**
+ * Indicates whether a new SD card was found.
+ */
+ bool new_sd_card_found;
+ /**
+ * Indicates whether the SD card is filled with random characters.
+ */
+ bool filled_with_random;
+ /**
+ * Indicates whether the stick has been initialized by generating
+ * the AES keys.
+ */
+ bool stick_initialized;
+ };
+
+ /**
* Set debug level of messages written on stderr
* @param state state=True - most messages, state=False - only errors level
*/
@@ -61,7 +132,28 @@ extern "C" {
* Set debug level of messages written on stderr
* @param level (int) 0-lowest verbosity, 5-highest verbosity
*/
- NK_C_API void NK_set_debug_level(const int level);
+ NK_C_API void NK_set_debug_level(const int level);
+
+ /**
+ * Get the major library version, e. g. the 3 in v3.2.
+ * @return the major library version
+ */
+ NK_C_API unsigned int NK_get_major_library_version();
+
+ /**
+ * Get the minor library version, e. g. the 2 in v3.2.
+ * @return the minor library version
+ */
+ NK_C_API unsigned int NK_get_minor_library_version();
+
+ /**
+ * Get the library version as a string. This is the output of
+ * `git describe --always` at compile time, for example "v3.3" or
+ * "v3.3-19-gaee920b".
+ * The return value is a string literal and must not be freed.
+ * @return the library version as a string
+ */
+ NK_C_API const char* NK_get_library_version();
/**
* Connect to device of given model. Currently library can be connected only to one device at once.
@@ -294,6 +386,19 @@ extern "C" {
*/
NK_C_API int NK_totp_set_time(uint64_t time);
+ /**
+ * Set the device time used for TOTP to the given time. Contrary to
+ * {@code set_time(uint64_t)}, this command fails if {@code old_time}
+ * &gt; {@code time} or if {@code old_time} is zero (where {@code
+ * old_time} is the current time on the device).
+ *
+ * @param time new device time as Unix timestamp (seconds since
+ * 1970-01-01)
+ * @return command processing error code
+ */
+ NK_C_API int NK_totp_set_time_soft(uint64_t time);
+
+ [[deprecated("NK_totp_get_time is deprecated -- use NK_totp_set_time_soft instead")]]
NK_C_API int NK_totp_get_time();
//passwords
@@ -587,6 +692,17 @@ extern "C" {
NK_C_API char* NK_get_status_storage_as_string();
/**
+ * Get the Storage stick status and return the command processing
+ * error code. If the code is zero, i. e. the command was successful,
+ * the storage status is written to the output pointer's target.
+ * The output pointer must not be null.
+ *
+ * @param out the output pointer for the storage status
+ * @return command processing error code
+ */
+ NK_C_API int NK_get_status_storage(struct NK_storage_status* out);
+
+ /**
* Get SD card usage attributes as string.
* Usable during hidden volumes creation.
* Storage only
diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc
index f86a3eb..ab4cac5 100644
--- a/NitrokeyManager.cc
+++ b/NitrokeyManager.cc
@@ -668,11 +668,15 @@ using nitrokey::misc::strcpyT;
return false;
}
- bool NitrokeyManager::get_time(uint64_t time) {
+ void NitrokeyManager::set_time_soft(uint64_t time) {
auto p = get_payload<SetTime>();
p.reset = 0;
p.time = time;
SetTime::CommandTransaction::run(device, p);
+ }
+
+ bool NitrokeyManager::get_time(uint64_t time) {
+ set_time_soft(time);
return true;
}
diff --git a/libnitrokey/NitrokeyManager.h b/libnitrokey/NitrokeyManager.h
index d4630b0..0689c3f 100644
--- a/libnitrokey/NitrokeyManager.h
+++ b/libnitrokey/NitrokeyManager.h
@@ -65,6 +65,18 @@ char * strndup(const char* str, size_t maxlen);
stick10::ReadSlot::ResponsePayload get_HOTP_slot_data(const uint8_t slot_number);
bool set_time(uint64_t time);
+ /**
+ * Set the device time used for TOTP to the given time. Contrary to
+ * {@code set_time(uint64_t)}, this command fails if {@code old_time}
+ * &gt; {@code time} or if {@code old_time} is zero (where {@code
+ * old_time} is the current time on the device).
+ *
+ * @param time new device time as Unix timestamp (seconds since
+ * 1970-01-01)
+ */
+ void set_time_soft(uint64_t time);
+
+ [[deprecated("get_time is deprecated -- use set_time_soft instead")]]
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);
diff --git a/libnitrokey/version.h b/libnitrokey/version.h
new file mode 100644
index 0000000..6547af0
--- /dev/null
+++ b/libnitrokey/version.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Nitrokey UG
+ *
+ * This file is part of libnitrokey.
+ *
+ * libnitrokey is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * libnitrokey is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0
+ */
+
+#ifndef LIBNITROKEY_VERSION_H
+#define LIBNITROKEY_VERSION_H
+
+namespace nitrokey {
+ unsigned int get_major_library_version();
+
+ unsigned int get_minor_library_version();
+
+ const char* get_library_version();
+}
+
+#endif
diff --git a/unittest/conftest.py b/unittest/conftest.py
index 9af67ac..35cc714 100644
--- a/unittest/conftest.py
+++ b/unittest/conftest.py
@@ -86,7 +86,7 @@ def C(request=None):
assert nk_login != 0 # returns 0 if not connected or wrong model or 1 when connected
global device_type
firmware_version = C.NK_get_minor_firmware_version()
- model = 'P' if firmware_version in [7,8] else 'S'
+ model = 'P' if firmware_version < 20 else 'S'
device_type = (model, firmware_version)
print('Connected device: {} {}'.format(model, firmware_version))
diff --git a/unittest/test_offline.cc b/unittest/test_offline.cc
index e34eeb4..9d2f195 100644
--- a/unittest/test_offline.cc
+++ b/unittest/test_offline.cc
@@ -161,6 +161,16 @@ TEST_CASE("Test device commands ids", "[fast]") {
}
+#include "version.h"
+TEST_CASE("Test version getter", "[fast]") {
+ REQUIRE(nitrokey::get_major_library_version() >= 3u);
+ REQUIRE(nitrokey::get_minor_library_version() >= 3u);
+ const char *library_version = nitrokey::get_library_version();
+ REQUIRE(library_version != nullptr);
+ std::string s = library_version;
+ REQUIRE(s.length() >= 8);
+ REQUIRE(s.find("g") != std::string::npos);
+}
TEST_CASE("Connect should not return true after the second attempt", "[fast]") {
int result = 0;
diff --git a/unittest/test_pro.py b/unittest/test_pro.py
index 53588f6..fb936f8 100644
--- a/unittest/test_pro.py
+++ b/unittest/test_pro.py
@@ -577,6 +577,55 @@ def test_get_code_user_authorize(C):
assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
+@pytest.mark.otp
+def test_authorize_issue_admin(C):
+ skip_if_device_version_lower_than({'S': 43, 'P': 9})
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ assert C.NK_write_config(255, 255, 255, True, False, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_first_authenticate(b"wrong pass", b"another temp pass") == DeviceErrorCode.WRONG_PASSWORD
+ assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_NOT_AUTHORIZED
+
+ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ assert C.NK_write_config(255, 255, 255, True, False, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+
+@pytest.mark.otp
+def test_authorize_issue_user(C):
+ skip_if_device_version_lower_than({'S': 43, 'P': 9}) # issue fixed in Pro v0.9, Storage version chosen arbitrary
+
+ assert C.NK_lock_device() == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ assert C.NK_write_totp_slot(0, b'python_otp_auth', bbRFC_SECRET, 30, True, False, False, b'',
+ DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ # enable PIN protection of OTP codes with write_config
+ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ assert C.NK_write_config(255, 255, 255, True, False, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ gs(C.NK_get_totp_code(0, 0, 0, 0))
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_NOT_AUTHORIZED
+
+ assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK
+ gs(C.NK_get_totp_code_PIN(0, 0, 0, 0, DefaultPasswords.USER_TEMP))
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
+
+ assert C.NK_user_authenticate(b"wrong pass", b"another temp pass") == DeviceErrorCode.WRONG_PASSWORD
+ gs(C.NK_get_totp_code_PIN(0, 0, 0, 0, DefaultPasswords.USER_TEMP))
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_NOT_AUTHORIZED
+
+ assert C.NK_user_authenticate(DefaultPasswords.USER, DefaultPasswords.USER_TEMP) == DeviceErrorCode.STATUS_OK
+ gs(C.NK_get_totp_code_PIN(0, 0, 0, 0, DefaultPasswords.USER_TEMP))
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
+
+ # disable PIN protection with write_config
+ assert C.NK_first_authenticate(DefaultPasswords.ADMIN, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ assert C.NK_write_config(255, 255, 255, False, True, DefaultPasswords.ADMIN_TEMP) == DeviceErrorCode.STATUS_OK
+ code = gs(C.NK_get_totp_code(0, 0, 0, 0))
+ assert code != b''
+ assert C.NK_get_last_command_status() == DeviceErrorCode.STATUS_OK
+
def cast_pointer_to_tuple(obj, typen, len):
# usage:
# config = cast_pointer_to_tuple(config_raw_data, 'uint8_t', 5)
diff --git a/version.cc.in b/version.cc.in
new file mode 100644
index 0000000..0eae647
--- /dev/null
+++ b/version.cc.in
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 Nitrokey UG
+ *
+ * This file is part of libnitrokey.
+ *
+ * libnitrokey is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * libnitrokey is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnitrokey. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0
+ */
+
+#include "version.h"
+
+namespace nitrokey {
+ unsigned int get_major_library_version() {
+ return @PROJECT_VERSION_MAJOR@;
+ }
+
+ unsigned int get_minor_library_version() {
+ return @PROJECT_VERSION_MINOR@;
+ }
+
+ const char* get_library_version() {
+ return "@PROJECT_VERSION_GIT@";
+ }
+}
+