diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | .gitmodules | 6 | ||||
-rw-r--r-- | Makefile | 36 | ||||
-rw-r--r-- | command_id.cc | 146 | ||||
-rw-r--r-- | crc32.cc | 37 | ||||
-rw-r--r-- | device.cc | 97 | ||||
-rw-r--r-- | include/command.h | 23 | ||||
-rw-r--r-- | include/command_id.h | 85 | ||||
-rw-r--r-- | include/crc32.h | 32 | ||||
-rw-r--r-- | include/cxx_semantics.h | 6 | ||||
-rw-r--r-- | include/device.h | 80 | ||||
-rw-r--r-- | include/device_proto.h | 1170 | ||||
-rw-r--r-- | include/dissect.h | 82 | ||||
-rw-r--r-- | include/inttypes.h | 522 | ||||
-rw-r--r-- | include/log.h | 50 | ||||
-rw-r--r-- | include/misc.h | 14 | ||||
-rw-r--r-- | include/stick10_commands.h | 588 | ||||
-rw-r--r-- | include/stick20_commands.h | 233 | ||||
-rw-r--r-- | log.cc | 42 | ||||
-rw-r--r-- | misc.cc | 49 | ||||
-rw-r--r-- | test.cc | 35 | ||||
m--------- | unittest/Catch | 0 | ||||
-rw-r--r-- | unittest/Makefile | 33 | ||||
l--------- | unittest/build/libnitrokey.so | 1 | ||||
-rw-r--r-- | unittest/test.cc | 80 | ||||
-rw-r--r-- | unittest/test_HOTP.cc | 101 |
26 files changed, 2368 insertions, 1183 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a8accf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.sw* +*.log +*.o diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c811cb7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "Catch"] + path = Catch + url = https://github.com/philsquared/Catch.git +[submodule "unittest/Catch"] + path = unittest/Catch + url = https://github.com/philsquared/Catch.git @@ -1,31 +1,43 @@ CC = $(PREFIX)-gcc -CXX = $(PREFIX)-g++ +#CXX = $(PREFIX)-g++ +CXX = clang++-3.6 LD = $(CXX) INCLUDE = -Iinclude/ LIB = -lhidapi-libusb BUILD = build -CXXFLAGS = -std=c++14 +CXXFLAGS = -std=c++14 -fPIC +SOFLAGS = -shared CXXSOURCES = $(wildcard *.cc) -OBJ = $(CXXSOURCES:%.cc:$(BUILD)/%.o) +OBJ = $(CXXSOURCES:%.cc=$(BUILD)/%.o) +DEPENDS = $(CXXSOURCES:%.cc=$(BUILD)/%.d) -$(BUILD)/libnitrokey.so: $(OBJ) - $(CXX) -shared $(OBJ) -o $@ +all: $(OBJ) $(BUILD)/libnitrokey.so unittest -$(BUILD)/%.o: %.cc - $(CXX) -c $< -o $@ $(INCLUDE) $(CXXFLAGS) +$(BUILD)/libnitrokey.so: $(OBJ) $(DEPENDS) + $(CXX) $(SOFLAGS) $(OBJ) $(LIB) -o $@ + +$(BUILD)/%.d: %.cc + $(CXX) -M $< -o $@ $(INCLUDE) $(CXXFLAGS) -all: $(OBJ) $(BUILD)/libnitrokey.so +$(BUILD)/%.o: %.cc $(DEPENDS) + $(CXX) -c $< -o $@ $(INCLUDE) $(CXXFLAGS) clean: - rm $(OBJ) - rm $(BUILD)/libnitrokey.so + rm -f $(OBJ) + rm -f $(BUILD)/libnitrokey.so + make -C unittest clean mrproper: clean - rm $(BUILD)/*.d + 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 +.PHONY: all clean mrproper unittest include $(wildcard build/*.d) diff --git a/command_id.cc b/command_id.cc new file mode 100644 index 0000000..9512b7d --- /dev/null +++ b/command_id.cc @@ -0,0 +1,146 @@ +#include <assert.h> +#include "command_id.h" + +namespace nitrokey { +namespace proto { + +const char *commandid_to_string(CommandID id) { + switch (id) { + case CommandID::GET_STATUS: + return "GET_STATUS"; + case CommandID::WRITE_TO_SLOT: + return "WRITE_TO_SLOT"; + case CommandID::READ_SLOT_NAME: + return "READ_SLOT_NAME"; + case CommandID::READ_SLOT: + return "READ_SLOT"; + case CommandID::GET_CODE: + return "GET_CODE"; + case CommandID::WRITE_CONFIG: + return "WRITE_CONFIG"; + case CommandID::ERASE_SLOT: + return "ERASE_SLOT"; + case CommandID::FIRST_AUTHENTICATE: + return "FIRST_AUTHENTICATE"; + case CommandID::AUTHORIZE: + return "AUTHORIZE"; + case CommandID::GET_PASSWORD_RETRY_COUNT: + return "GET_PASSWORD_RETRY_COUNT"; + case CommandID::CLEAR_WARNING: + return "CLEAR_WARNING"; + case CommandID::SET_TIME: + return "SET_TIME"; + case CommandID::TEST_COUNTER: + return "TEST_COUNTER"; + case CommandID::TEST_TIME: + return "TEST_TIME"; + case CommandID::USER_AUTHENTICATE: + return "USER_AUTHENTICATE"; + case CommandID::GET_USER_PASSWORD_RETRY_COUNT: + return "GET_USER_PASSWORD_RETRY_COUNT"; + case CommandID::USER_AUTHORIZE: + return "USER_AUTHORIZE"; + case CommandID::UNLOCK_USER_PASSWORD: + return "UNLOCK_USER_PASSWORD"; + case CommandID::LOCK_DEVICE: + return "LOCK_DEVICE"; + case CommandID::FACTORY_RESET: + return "FACTORY_RESET"; + case CommandID::CHANGE_USER_PIN: + return "CHANGE_USER_PIN"; + case CommandID::CHANGE_ADMIN_PIN: + return "CHANGE_ADMIN_PIN"; + + case CommandID::ENABLE_CRYPTED_PARI: + return "ENABLE_CRYPTED_PARI"; + case CommandID::DISABLE_CRYPTED_PARI: + return "DISABLE_CRYPTED_PARI"; + case CommandID::ENABLE_HIDDEN_CRYPTED_PARI: + return "ENABLE_HIDDEN_CRYPTED_PARI"; + case CommandID::DISABLE_HIDDEN_CRYPTED_PARI: + return "DISABLE_HIDDEN_CRYPTED_PARI"; + case CommandID::ENABLE_FIRMWARE_UPDATE: + return "ENABLE_FIRMWARE_UPDATE"; + case CommandID::EXPORT_FIRMWARE_TO_FILE: + return "EXPORT_FIRMWARE_TO_FILE"; + case CommandID::GENERATE_NEW_KEYS: + return "GENERATE_NEW_KEYS"; + case CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS: + return "FILL_SD_CARD_WITH_RANDOM_CHARS"; + + case CommandID::WRITE_STATUS_DATA: + return "WRITE_STATUS_DATA"; + case CommandID::ENABLE_READONLY_UNCRYPTED_LUN: + return "ENABLE_READONLY_UNCRYPTED_LUN"; + case CommandID::ENABLE_READWRITE_UNCRYPTED_LUN: + return "ENABLE_READWRITE_UNCRYPTED_LUN"; + + case CommandID::SEND_PASSWORD_MATRIX: + return "SEND_PASSWORD_MATRIX"; + case CommandID::SEND_PASSWORD_MATRIX_PINDATA: + return "SEND_PASSWORD_MATRIX_PINDATA"; + case CommandID::SEND_PASSWORD_MATRIX_SETUP: + return "SEND_PASSWORD_MATRIX_SETUP"; + + case CommandID::GET_DEVICE_STATUS: + return "GET_DEVICE_STATUS"; + case CommandID::SEND_DEVICE_STATUS: + return "SEND_DEVICE_STATUS"; + + case CommandID::SEND_HIDDEN_VOLUME_PASSWORD: + return "SEND_HIDDEN_VOLUME_PASSWORD"; + case CommandID::SEND_HIDDEN_VOLUME_SETUP: + return "SEND_HIDDEN_VOLUME_SETUP"; + case CommandID::SEND_PASSWORD: + return "SEND_PASSWORD"; + case CommandID::SEND_NEW_PASSWORD: + return "SEND_NEW_PASSWORD"; + case CommandID::CLEAR_NEW_SD_CARD_FOUND: + return "CLEAR_NEW_SD_CARD_FOUND"; + + case CommandID::SEND_STARTUP: + return "SEND_STARTUP"; + case CommandID::SEND_CLEAR_STICK_KEYS_NOT_INITIATED: + return "SEND_CLEAR_STICK_KEYS_NOT_INITIATED"; + case CommandID::SEND_LOCK_STICK_HARDWARE: + return "SEND_LOCK_STICK_HARDWARE"; + + case CommandID::PRODUCTION_TEST: + return "PRODUCTION_TEST"; + case CommandID::SEND_DEBUG_DATA: + return "SEND_DEBUG_DATA"; + + case CommandID::CHANGE_UPDATE_PIN: + return "CHANGE_UPDATE_PIN"; + + case CommandID::GET_PW_SAFE_SLOT_STATUS: + return "GET_PW_SAFE_SLOT_STATUS"; + case CommandID::GET_PW_SAFE_SLOT_NAME: + return "GET_PW_SAFE_SLOT_NAME"; + case CommandID::GET_PW_SAFE_SLOT_PASSWORD: + return "GET_PW_SAFE_SLOT_PASSWORD"; + case CommandID::GET_PW_SAFE_SLOT_LOGINNAME: + return "GET_PW_SAFE_SLOT_LOGINNAME"; + case CommandID::SET_PW_SAFE_SLOT_DATA_1: + return "SET_PW_SAFE_SLOT_DATA_1"; + case CommandID::SET_PW_SAFE_SLOT_DATA_2: + return "SET_PW_SAFE_SLOT_DATA_2"; + case CommandID::PW_SAFE_ERASE_SLOT: + return "PW_SAFE_ERASE_SLOT"; + case CommandID::PW_SAFE_ENABLE: + return "PW_SAFE_ENABLE"; + case CommandID::PW_SAFE_INIT_KEY: + return "PW_SAFE_INIT_KEY"; + case CommandID::PW_SAFE_SEND_DATA: + return "PW_SAFE_SEND_DATA"; + case CommandID::SD_CARD_HIGH_WATERMARK: + return "SD_CARD_HIGH_WATERMARK"; + case CommandID::DETECT_SC_AES: + return "DETECT_SC_AES"; + case CommandID::NEW_AES_KEY: + return "NEW_AES_KEY"; + } + return "UNKNOWN"; +} +} +} diff --git a/crc32.cc b/crc32.cc deleted file mode 100644 index 558d4ef..0000000 --- a/crc32.cc +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Author: Copyright (C) Andrzej Surowiec 2012 - * - * - * This file is part of Nitrokey. - * - * Nitrokey is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * Nitrokey 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 General Public License - * along with Nitrokey. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "crc32.h" - - -uint32_t Crc32 (uint32_t Crc, uint32_t Data) -{ -int i; - - Crc = Crc ^ Data; - - for (i = 0; i < 32; i++) - if (Crc & 0x80000000) - Crc = (Crc << 1) ^ 0x04C11DB7; // Polynomial used in STM32 - else - Crc = (Crc << 1); - - return (Crc); -} @@ -1,52 +1,93 @@ #include <chrono> #include <thread> #include <cstddef> +#include <stdexcept> #include <hidapi/hidapi.h> #include "device.h" +#include "log.h" +#include "misc.h" -using namespace device; +using namespace nitrokey::device; +using namespace nitrokey::log; Device::Device() -: m_vid(0), m_pid(0), - m_retry_count(4), - m_retry_timeout(50), - mp_devhandle(NULL) {} + : m_vid(0), + m_pid(0), + m_retry_count(40), + m_retry_timeout(50), + mp_devhandle(NULL) {} +bool Device::disconnect() { + Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2); + + hid_exit(); + mp_devhandle = NULL; + return true; +} bool Device::connect() { - hid_init(); + Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2); - mp_devhandle = hid_open(m_vid, m_pid, NULL); - return mp_devhandle != NULL; + // hid_init(); + mp_devhandle = hid_open(m_vid, m_pid, NULL); + // hid_init(); + return mp_devhandle != NULL; } CommError Device::send(const void *packet) { - return (CommError)( - hid_send_feature_report(mp_devhandle, (const unsigned char *)(packet), - HID_REPORT_SIZE)); + Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2); + + if (mp_devhandle == NULL) + throw std::runtime_error("Attempted HID send on an invalid descriptor."); + + return (CommError)(hid_send_feature_report( + mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE)); } CommError Device::recv(void *packet) { - CommError status; - int retry_count = 0; - - for(;;) { - status = (CommError)( - hid_get_feature_report(mp_devhandle, (unsigned char *)(packet), - HID_REPORT_SIZE)); - if (status == CommError::ERR_NO_ERROR || retry_count++ >= m_retry_count) - break; - std::this_thread::sleep_for(m_retry_timeout); - } - - return status; + // FIXME change CommError return value to int (return value of + // hid_get_feature_report) + 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 (;;) { + 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::string herr(wherr.begin(), wherr.end()); + Log::instance()(std::string("libhid error message: ") + herr, + Loglevel::DEBUG_L2); + + if (status > 0) break; // success + if (retry_count++ >= m_retry_count) { + Log::instance()( + "Maximum retry count reached" + std::to_string(retry_count), + Loglevel::WARNING); + break; + } + Log::instance()("Retrying... " + std::to_string(retry_count), + Loglevel::DEBUG); + std::this_thread::sleep_for(m_retry_timeout); + } + + return (CommError)status; } Stick10::Stick10() { - m_vid = 0x20a0; - m_pid = 0x4108; + m_vid = 0x20a0; + m_pid = 0x4108; } Stick20::Stick20() { - m_vid = 0x20a0; - m_pid = 0x4109; + m_vid = 0x20a0; + m_pid = 0x4109; } diff --git a/include/command.h b/include/command.h new file mode 100644 index 0000000..715902d --- /dev/null +++ b/include/command.h @@ -0,0 +1,23 @@ +#ifndef COMMAND_H +#define COMMAND_H +#include <string> +#include "command_id.h" +#include "cxx_semantics.h" + +namespace nitrokey { +namespace proto { + +template <CommandID cmd_id> +class Command : semantics::non_constructible { + public: + constexpr static CommandID command_id() { return cmd_id; } + + template <typename T> + static std::string dissect(const T &) { + return std::string("Payload dissection is unavailable"); + } +}; +} +} + +#endif diff --git a/include/command_id.h b/include/command_id.h new file mode 100644 index 0000000..37aa5d2 --- /dev/null +++ b/include/command_id.h @@ -0,0 +1,85 @@ +#ifndef COMMAND_ID_H +#define COMMAND_ID_H +#include "inttypes.h" + +namespace nitrokey { +namespace proto { + +enum class CommandID : uint8_t { + GET_STATUS = 0x00, + WRITE_TO_SLOT = 0x01, + READ_SLOT_NAME = 0x02, + READ_SLOT = 0x03, + GET_CODE = 0x04, + WRITE_CONFIG = 0x05, + ERASE_SLOT = 0x06, + FIRST_AUTHENTICATE = 0x07, + AUTHORIZE = 0x08, + GET_PASSWORD_RETRY_COUNT = 0x09, + CLEAR_WARNING = 0x0A, + SET_TIME = 0x0B, + TEST_COUNTER = 0x0C, + TEST_TIME = 0x0D, + USER_AUTHENTICATE = 0x0E, + GET_USER_PASSWORD_RETRY_COUNT = 0x0F, + USER_AUTHORIZE = 0x10, + UNLOCK_USER_PASSWORD = 0x11, + LOCK_DEVICE = 0x12, + FACTORY_RESET = 0x13, + CHANGE_USER_PIN = 0x14, + CHANGE_ADMIN_PIN = 0x15, + + ENABLE_CRYPTED_PARI = 0x20, + DISABLE_CRYPTED_PARI, + ENABLE_HIDDEN_CRYPTED_PARI, + DISABLE_HIDDEN_CRYPTED_PARI, + ENABLE_FIRMWARE_UPDATE, + EXPORT_FIRMWARE_TO_FILE, + GENERATE_NEW_KEYS, + FILL_SD_CARD_WITH_RANDOM_CHARS, + + WRITE_STATUS_DATA, + ENABLE_READONLY_UNCRYPTED_LUN, + ENABLE_READWRITE_UNCRYPTED_LUN, + + SEND_PASSWORD_MATRIX, + SEND_PASSWORD_MATRIX_PINDATA, + SEND_PASSWORD_MATRIX_SETUP, + + GET_DEVICE_STATUS, + SEND_DEVICE_STATUS, + + SEND_HIDDEN_VOLUME_PASSWORD, + SEND_HIDDEN_VOLUME_SETUP, + SEND_PASSWORD, + SEND_NEW_PASSWORD, + CLEAR_NEW_SD_CARD_FOUND, + + SEND_STARTUP, + SEND_CLEAR_STICK_KEYS_NOT_INITIATED, + SEND_LOCK_STICK_HARDWARE, + + PRODUCTION_TEST, + SEND_DEBUG_DATA, + + CHANGE_UPDATE_PIN, + + GET_PW_SAFE_SLOT_STATUS = 0x60, + GET_PW_SAFE_SLOT_NAME = 0x61, + GET_PW_SAFE_SLOT_PASSWORD = 0x62, + GET_PW_SAFE_SLOT_LOGINNAME = 0x63, + SET_PW_SAFE_SLOT_DATA_1 = 0x64, + SET_PW_SAFE_SLOT_DATA_2 = 0x65, + PW_SAFE_ERASE_SLOT = 0x66, + PW_SAFE_ENABLE = 0x67, + PW_SAFE_INIT_KEY = 0x68, + PW_SAFE_SEND_DATA = 0x69, + SD_CARD_HIGH_WATERMARK = 0x70, + DETECT_SC_AES = 0x6a, + NEW_AES_KEY = 0x6b +}; + +const char *commandid_to_string(CommandID id); +} +} +#endif diff --git a/include/crc32.h b/include/crc32.h deleted file mode 100644 index b0e16ff..0000000 --- a/include/crc32.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Author: Copyright (C) Andrzej Surowiec 2012 - * - * - * This file is part of Nitrokey. - * - * Nitrokey is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * Nitrokey 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 General Public License - * along with Nitrokey. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef CRC32_H -#define CRC32_H - -#ifdef _MSC_VER -#define uint32_t unsigned long -#else -#include "inttypes.h" -#endif - -uint32_t Crc32 (uint32_t Crc, uint32_t Data); - -#endif // CRC32_H diff --git a/include/cxx_semantics.h b/include/cxx_semantics.h index f5d3dca..b846317 100644 --- a/include/cxx_semantics.h +++ b/include/cxx_semantics.h @@ -7,9 +7,9 @@ * There's no need to include Boost for a simple subset this project needs. */ namespace semantics { - class non_constructible { - non_constructible() {} - }; +class non_constructible { + non_constructible() {} +}; } #endif diff --git a/include/device.h b/include/device.h index b2d83b5..dc7832d 100644 --- a/include/device.h +++ b/include/device.h @@ -8,58 +8,66 @@ // TODO !! SEMAPHORE +namespace nitrokey { namespace device { -enum class CommError -{ - ERR_NO_ERROR = 0, - ERR_NOT_CONNECTED = -1, - ERR_WRONG_RESPONSE_CRC = -2, - ERR_SENDING = -3, - ERR_STATUS_NOT_OK = -4 + +enum class CommError { + ERR_NO_ERROR = 0, + ERR_NOT_CONNECTED = -1, + ERR_WRONG_RESPONSE_CRC = -2, + ERR_SENDING = -3, + ERR_STATUS_NOT_OK = -4 }; class Device { -public: - Device(); + public: + Device(); + + // lack of device is not actually an error, + // so it doesn't throw + virtual bool connect(); + virtual bool disconnect(); - // lack of device is not actually an error, - // so it doesn't throw - virtual bool connect(); + /* + * Sends packet of HID_REPORT_SIZE. + */ + virtual CommError send(const void *packet); - /* - * Sends packet of HID_REPORT_SIZE. - */ - virtual CommError send(const void *packet); + /* + * Gets packet of HID_REPORT_SIZE. + * Can sleep. See below. + */ + virtual CommError recv(void *packet); - /* - * Gets packet of HID_REPORT_SIZE. - * Can sleep. See below. - */ - virtual CommError recv(void *packet); + int get_retry_count() { return m_retry_count; } + const; + std::chrono::milliseconds get_retry_timeout() { return m_retry_timeout; } + const; -protected: - uint16_t m_vid; - uint16_t m_pid; + protected: + uint16_t m_vid; + uint16_t m_pid; - /* - * While the project uses Signal11 portable HIDAPI - * library, there's no way of doing it asynchronously, - * hence polling. - */ - int m_retry_count; - std::chrono::milliseconds m_retry_timeout; + /* + * While the project uses Signal11 portable HIDAPI + * library, there's no way of doing it asynchronously, + * hence polling. + */ + int m_retry_count; + std::chrono::milliseconds m_retry_timeout; - hid_device *mp_devhandle; + hid_device *mp_devhandle; }; class Stick10 : public Device { -public: - Stick10(); + public: + Stick10(); }; class Stick20 : public Device { -public: - Stick20(); + public: + Stick20(); }; } +} #endif diff --git a/include/device_proto.h b/include/device_proto.h index fb1dd4e..e05a303 100644 --- a/include/device_proto.h +++ b/include/device_proto.h @@ -1,6 +1,7 @@ #ifndef DEVICE_PROTO_H #define DEVICE_PROTO_H #include <utility> +#include <thread> #include <type_traits> #include <stdexcept> #include <string> @@ -9,7 +10,10 @@ #include "inttypes.h" #include "cxx_semantics.h" #include "device.h" -#include "utils/crc32.h" +#include "misc.h" +#include "log.h" +#include "command_id.h" +#include "dissect.h" #define STICK20_UPDATE_MODE_VID 0x03EB #define STICK20_UPDATE_MODE_PID 0x2FF1 @@ -20,99 +24,13 @@ #define PWS_PASSWORD_LENGTH 20 #define PWS_LOGINNAME_LENGTH 32 -#define PWS_SEND_PASSWORD 0 -#define PWS_SEND_LOGINNAME 1 -#define PWS_SEND_TAB 2 -#define PWS_SEND_CR 3 - -namespace device { - class Device; - enum class CommError; -} - -static inline uint32_t crc(const uint8_t *data, size_t size) { - uint32_t crc = 0xffffffff; - const uint32_t *pend = (const uint32_t *)(data + size); - for (const uint32_t *p = (const uint32_t *)(data); p < pend; p++) - crc = Crc32 (crc, *p); - return crc; -} +#define PWS_SEND_PASSWORD 0 +#define PWS_SEND_LOGINNAME 1 +#define PWS_SEND_TAB 2 +#define PWS_SEND_CR 3 +namespace nitrokey { namespace proto { -enum class CommandID : uint8_t { - GET_STATUS = 0x00, - WRITE_TO_SLOT = 0x01, - READ_SLOT_NAME = 0x02, - READ_SLOT = 0x03, - GET_CODE = 0x04, - WRITE_CONFIG = 0x05, - ERASE_SLOT = 0x06, - FIRST_AUTHENTICATE = 0x07, - AUTHORIZE = 0x08, - GET_PASSWORD_RETRY_COUNT = 0x09, - CLEAR_WARNING = 0x0A, - SET_TIME = 0x0B, - TEST_COUNTER = 0x0C, - TEST_TIME = 0x0D, - USER_AUTHENTICATE = 0x0E, - GET_USER_PASSWORD_RETRY_COUNT = 0x0F, - USER_AUTHORIZE = 0x10, - UNLOCK_USER_PASSWORD = 0x11, - LOCK_DEVICE = 0x12, - FACTORY_RESET = 0x13, - CHANGE_USER_PIN = 0x14, - CHANGE_ADMIN_PIN = 0x15, - - ENABLE_CRYPTED_PARI = 0x20, - DISABLE_CRYPTED_PARI, - ENABLE_HIDDEN_CRYPTED_PARI, - DISABLE_HIDDEN_CRYPTED_PARI, - ENABLE_FIRMWARE_UPDATE, - EXPORT_FIRMWARE_TO_FILE, - GENERATE_NEW_KEYS, - FILL_SD_CARD_WITH_RANDOM_CHARS, - - WRITE_STATUS_DATA, - ENABLE_READONLY_UNCRYPTED_LUN, - ENABLE_READWRITE_UNCRYPTED_LUN, - - SEND_PASSWORD_MATRIX, - SEND_PASSWORD_MATRIX_PINDATA, - SEND_PASSWORD_MATRIX_SETUP, - - GET_DEVICE_STATUS, - SEND_DEVICE_STATUS, - - SEND_HIDDEN_VOLUME_PASSWORD, - SEND_HIDDEN_VOLUME_SETUP, - SEND_PASSWORD, - SEND_NEW_PASSWORD, - CLEAR_NEW_SD_CARD_FOUND, - - SEND_STARTUP, - SEND_CLEAR_STICK_KEYS_NOT_INITIATED, - SEND_LOCK_STICK_HARDWARE, - - PRODUCTION_TEST, - SEND_DEBUG_DATA, - - CHANGE_UPDATE_PIN, - - GET_PW_SAFE_SLOT_STATUS = 0x60, - GET_PW_SAFE_SLOT_NAME = 0x61, - GET_PW_SAFE_SLOT_PASSWORD = 0x62, - GET_PW_SAFE_SLOT_LOGINNAME = 0x63, - SET_PW_SAFE_SLOT_DATA_1 = 0x64, - SET_PW_SAFE_SLOT_DATA_2 = 0x65, - PW_SAFE_ERASE_SLOT = 0x66, - PW_SAFE_ENABLE = 0x67, - PW_SAFE_INIT_KEY = 0x68, - PW_SAFE_SEND_DATA = 0x69, - SD_CARD_HIGH_WATERMARK = 0x70, - DETECT_SC_AES = 0x6a, - NEW_AES_KEY = 0x6b -}; - /* * POD types for HID proto commands * Instances are meant to be __packed. @@ -125,39 +43,42 @@ enum class CommandID : uint8_t { */ template <CommandID cmd_id, typename Payload> struct HIDReport { - uint8_t _zero; - CommandID command_id; // uint8_t - union { - uint8_t _padding[HID_REPORT_SIZE - 6]; - Payload payload; - } __packed; - uint32_t crc; - - // POD types can't have non-default constructors - // used in Transaction<>::run() - void initialize() { - bzero(this, sizeof *this); - command_id = cmd_id; - } - - uint32_t calculate_CRC() const { - // w/o leading zero, a part of each HID packet - // w/o 4-byte crc - return ::crc((const uint8_t *)(this) + 1, - (size_t)(HID_REPORT_SIZE - 5)); - } - - void update_CRC() { - crc = calculate_CRC(); - } - - bool isCRCcorrect() const { - return crc == calculate_CRC(); - } - - bool isValid() const { - return !_zero && payload.isValid() && isCRCcorrect(); - } + uint8_t _zero; + CommandID command_id; // uint8_t + union { + uint8_t _padding[HID_REPORT_SIZE - 6]; + Payload payload; + } __packed; + uint32_t crc; + + // POD types can't have non-default constructors + // used in Transaction<>::run() + void initialize() { + bzero(this, sizeof *this); + command_id = cmd_id; + } + + uint32_t calculate_CRC() const { + // w/o leading zero, a part of each HID packet + // w/o 4-byte crc + return misc::stm_crc32((const uint8_t *)(this) + 1, + (size_t)(HID_REPORT_SIZE - 5)); + } + + void update_CRC() { crc = calculate_CRC(); } + + bool isCRCcorrect() const { return crc == calculate_CRC(); } + + bool isValid() const { + return true; + // return !_zero && payload.isValid() && isCRCcorrect(); + } + + operator std::string() const { + // Packet type is known upfront in normal operation. + // Can't be used to dissect random packets. + return QueryDissector<cmd_id, decltype(*this)>::dissect(*this); + } } __packed; /* @@ -166,890 +87,139 @@ struct HIDReport { * command_id member in incoming HIDReport structure carries the command * type last used. */ -template <typename ResponsePayload> +template <CommandID cmd_id, typename ResponsePayload> struct DeviceResponse { - uint8_t _zero; - uint8_t device_status; - uint8_t last_command_type; - uint32_t last_command_crc; - uint8_t last_command_status; - union { - uint8_t _padding[HID_REPORT_SIZE - 12]; - ResponsePayload payload; - } __packed; - uint32_t crc; - - void initialize() { - bzero(this, sizeof *this); - } - - uint32_t calculate_CRC() const { - // w/o leading zero, a part of each HID packet - // w/o 4-byte crc - return ::crc((const uint8_t *)(this) + 1, - (size_t)(HID_REPORT_SIZE - 5)); - } - - void update_CRC() { - crc = calculate_CRC(); - } - - bool isCRCcorrect() const { - return crc == calculate_CRC(); - } - - bool isValid() const { - return !_zero && payload.isValid() && isCRCcorrect(); - } + uint8_t _zero; + uint8_t device_status; + uint8_t command_id; // originally last_command_type + uint32_t last_command_crc; + uint8_t last_command_status; + union { + uint8_t _padding[HID_REPORT_SIZE - 12]; + ResponsePayload payload; + } __packed; + uint32_t crc; + + void initialize() { bzero(this, sizeof *this); } + + uint32_t calculate_CRC() const { + // w/o leading zero, a part of each HID packet + // w/o 4-byte crc + return misc::stm_crc32((const uint8_t *)(this) + 1, + (size_t)(HID_REPORT_SIZE - 5)); + } + + void update_CRC() { crc = calculate_CRC(); } + + bool isCRCcorrect() const { return crc == calculate_CRC(); } + + bool isValid() const { + // return !_zero && payload.isValid() && isCRCcorrect() && + // command_id == (uint8_t)(cmd_id); + return true; + } + + operator std::string() const { + return ResponseDissector<cmd_id, decltype(*this)>::dissect(*this); + } } __packed; struct EmptyPayload { - uint8_t _data[]; + uint8_t _data[]; + + bool isValid() const { return true; } - bool isValid() const { - return true; - } + std::string dissect() const { return std::string("Empty Payload."); } } __packed; -template<CommandID cmd_id, typename command_payload, - typename response_payload> +template <CommandID cmd_id, typename command_payload, typename response_payload> class Transaction : semantics::non_constructible { -public: - // Types declared in command class scope can't be reached from there. - typedef command_payload CommandPayload; - typedef response_payload ResponsePayload; - - typedef struct HIDReport<cmd_id, CommandPayload> OutgoingPacket; - typedef struct DeviceResponse<ResponsePayload> ResponsePacket; - - static_assert(std::is_pod<OutgoingPacket>::value, - "OutgoingPacket must be a POD type"); - static_assert(std::is_pod<ResponsePacket>::value, - "ResponsePacket must be a POD type"); - static_assert(sizeof(OutgoingPacket) == HID_REPORT_SIZE, - "OutgoingPacket type is not the right size"); - static_assert(sizeof(ResponsePacket) == HID_REPORT_SIZE, - "ResponsePacket type is not the right size"); - - static response_payload run(device::Device &dev, - const command_payload &payload) { - using namespace device; - - CommError status; - OutgoingPacket outp; - ResponsePacket resp; - - // POD types can't have non-default constructors - outp.initialize(); - resp.initialize(); - - outp.payload = payload; - outp.update_CRC(); - - if (!outp.isValid()) - throw std::runtime_error("Invalid outgoing packet"); - - status = dev.send(&outp); - if ((int)(status) < 0 && status != CommError::ERR_NO_ERROR) - throw std::runtime_error( - std::string("Device error while sending command ") + - std::to_string((int)(status))); - - status = dev.recv(&resp); - if ((int)(status) < 0 && status != CommError::ERR_NO_ERROR) - throw std::runtime_error( - std::string("Device error while executing command ") + - std::to_string((int)(status))); - - if (!resp.isValid()) - throw std::runtime_error("Invalid incoming packet"); - - // See: DeviceResponse - return resp.payload; - } - - static response_payload run(device::Device &dev) { - command_payload empty_payload; - return run(dev, empty_payload); - } + public: + // Types declared in command class scope can't be reached from there. + typedef command_payload CommandPayload; + typedef response_payload ResponsePayload; + + typedef struct HIDReport<cmd_id, CommandPayload> OutgoingPacket; + typedef struct DeviceResponse<cmd_id, ResponsePayload> ResponsePacket; + + static_assert(std::is_pod<OutgoingPacket>::value, + "outgoingpacket must be a pod type"); + static_assert(std::is_pod<ResponsePacket>::value, + "ResponsePacket must be a POD type"); + static_assert(sizeof(OutgoingPacket) == HID_REPORT_SIZE, + "OutgoingPacket type is not the right size"); + static_assert(sizeof(ResponsePacket) == HID_REPORT_SIZE, + "ResponsePacket type is not the right size"); + + static uint32_t getCRC( + const command_payload &payload) { + OutgoingPacket outp; + outp.initialize(); + outp.payload = payload; + outp.update_CRC(); + return outp.crc; + } + + static response_payload run(device::Device &dev, + const command_payload &payload) { + using namespace ::nitrokey::device; + using namespace ::nitrokey::log; + + Log::instance()(__PRETTY_FUNCTION__, Loglevel::DEBUG_L2); + + CommError status; + OutgoingPacket outp; + ResponsePacket resp; + + // POD types can't have non-default constructors + outp.initialize(); + resp.initialize(); + + outp.payload = payload; + outp.update_CRC(); + + Log::instance()("Outgoing HID packet:", Loglevel::DEBUG); + Log::instance()((std::string)(outp), Loglevel::DEBUG); + + if (!outp.isValid()) throw std::runtime_error("Invalid outgoing packet"); + + status = dev.send(&outp); + if ((int)(status) < 0 && status != CommError::ERR_NO_ERROR) + throw std::runtime_error( + std::string("Device error while sending command ") + + std::to_string((int)(status))); + + // FIXME make checks done in device:recv here + int retry = dev.get_retry_count(); + while (retry-- > 0) { + status = dev.recv(&resp); + if (resp.device_status == 0) break; + Log::instance()("Device status is not ready (CRC error?) retrying..", + Loglevel::DEBUG); // FIXME translate device_status to log + Log::instance()("Invalid incoming HID packet:", Loglevel::DEBUG_L2); + Log::instance()((std::string)(resp), Loglevel::DEBUG_L2); + std::this_thread::sleep_for(dev.get_retry_timeout()); + continue; + } + if ((int)(status) < 0 && status != CommError::ERR_NO_ERROR) + throw std::runtime_error( + std::string("Device error while executing command ") + + std::to_string((int)(status))); + + Log::instance()("Incoming HID packet:", Loglevel::DEBUG); + Log::instance()((std::string)(resp), Loglevel::DEBUG); + + if (!resp.isValid()) throw std::runtime_error("Invalid incoming packet"); + + // See: DeviceResponse + return resp.payload; + } + + static response_payload run(device::Device &dev) { + command_payload empty_payload; + return run(dev, empty_payload); + } }; - -/* - * Stick10 protocol definition - */ -namespace stick10 { - namespace command { - class GetSlotName : semantics::non_constructible { - public: - // reachable as a typedef in Transaction - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_name[15]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::READ_SLOT_NAME, - struct CommandPayload, - struct ResponsePayload> CommandTransaction; - }; - - class EraseSlot : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::ERASE_SLOT, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SetTime : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t reset; // 0 - get time, 1 - set time - uint64_t time; // posix time - - bool isValid() const { - return reset && reset != 1; - } - } __packed; - - typedef Transaction<CommandID::SET_TIME, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - // TODO duplicate TOTP - class WriteToHOTPSlot : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[15]; - uint8_t slot_secret[20]; - uint8_t slot_config; - uint8_t slot_token_id[13]; - uint8_t slot_counter[8]; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::WRITE_TO_SLOT, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class WriteToTOTPSlot : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[15]; - uint8_t slot_secret[20]; - uint8_t slot_config; - uint8_t slot_token_id[13]; - uint16_t slot_interval; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::WRITE_TO_SLOT, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class GetCode : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - uint64_t challenge; - uint64_t last_totp_time; - uint8_t last_interval; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - struct ResponsePayload { - uint8_t code[18]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_CODE, - struct CommandPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetHOTP : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::GET_CODE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class ReadSlot : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_name[15]; - uint8_t config; - uint8_t token_id[13]; - uint64_t counter; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::READ_SLOT, - struct CommandPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetStatus : semantics::non_constructible { - public: - struct ResponsePayload { - uint16_t firmware_version; - uint8_t card_serial[4]; - uint8_t general_config[3]; - uint8_t otp_password_config[2]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_STATUS, - struct EmptyPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetPasswordRetryCount : semantics::non_constructible { - public: - struct ResponsePayload { - uint8_t password_retry_count; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_PASSWORD_RETRY_COUNT, - struct EmptyPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetUserPasswordRetryCount : semantics::non_constructible { - public: - struct ResponsePayload { - uint8_t password_retry_count; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_USER_PASSWORD_RETRY_COUNT, - struct EmptyPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetPasswordSafeSlotStatus : semantics::non_constructible { - public: - struct ResponsePayload { - uint8_t password_safe_status[PWS_SLOT_COUNT]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_PW_SAFE_SLOT_STATUS, - struct EmptyPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetPasswordSafeSlotName : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_name[PWS_SLOTNAME_LENGTH]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_PW_SAFE_SLOT_NAME, - struct CommandPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetPasswordSafeSlotPassword : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_password[PWS_PASSWORD_LENGTH]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_PW_SAFE_SLOT_PASSWORD, - struct CommandPayload, - struct ResponsePayload> CommandTransaction; - }; - - class GetPasswordSafeSlotLogin : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - struct ResponsePayload { - uint8_t slot_login[PWS_LOGINNAME_LENGTH]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::GET_PW_SAFE_SLOT_LOGINNAME, - struct CommandPayload, - struct ResponsePayload> CommandTransaction; - }; - - class SetPasswordSafeSlotData : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[PWS_SLOTNAME_LENGTH]; - uint8_t slot_password[PWS_PASSWORD_LENGTH]; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::SET_PW_SAFE_SLOT_DATA_1, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SetPasswordSafeSlotData2 : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_name[PWS_SLOTNAME_LENGTH]; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::SET_PW_SAFE_SLOT_DATA_2, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class ErasePasswordSafeSlot : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::PW_SAFE_ERASE_SLOT, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class EnablePasswordSafe : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::PW_SAFE_ENABLE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class PasswordSafeInitKey : semantics::non_constructible { - public: - typedef Transaction<CommandID::PW_SAFE_INIT_KEY, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - // TODO naming screwed up, see above - class PasswordSafeSendSlotViaHID: semantics::non_constructible { - public: - struct CommandPayload { - uint8_t slot_number; - uint8_t slot_kind; - - bool isValid() const { - return !(slot_number & 0xF0); - } - } __packed; - - typedef Transaction<CommandID::PW_SAFE_SEND_DATA, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - - // TODO "Device::passwordSafeSendSlotDataViaHID" - - class WriteGeneralConfig : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t config[5]; - } __packed; - - typedef Transaction<CommandID::WRITE_CONFIG, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class FirstAuthenticate : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t card_password[25]; - uint8_t temporary_password[25]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::FIRST_AUTHENTICATE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class UserAuthenticate : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t card_password[25]; - uint8_t temporary_password[25]; - - bool isValid() const { - return true; - } - } __packed; - - typedef Transaction<CommandID::USER_AUTHENTICATE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class Authorize : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t crc[4]; - uint8_t password[25]; - } __packed; - - typedef Transaction<CommandID::AUTHORIZE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class UserAuthorize : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t crc[4]; - uint8_t password[25]; - } __packed; - - typedef Transaction<CommandID::USER_AUTHORIZE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class UnlockUserPassword : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t admin_password[20]; // TODO - } __packed; - - // TODO could we get the stick to return the retry count? - - typedef Transaction<CommandID::UNLOCK_USER_PASSWORD, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class ChangeUserPin : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t old_pin[25]; - uint8_t new_pin[25]; - } __packed; - - typedef Transaction<CommandID::CHANGE_USER_PIN, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - // TODO why is it needed? - class IsAESSupported : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[20]; - } __packed; - - typedef Transaction<CommandID::DETECT_SC_AES, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class ChangeAdminPin : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t old_pin[25]; - uint8_t new_pin[25]; - } __packed; - - typedef Transaction<CommandID::CHANGE_ADMIN_PIN, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class LockDevice : semantics::non_constructible { - public: - typedef Transaction<CommandID::LOCK_DEVICE, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - class FactoryReset : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[20]; - } __packed; - - typedef Transaction<CommandID::FACTORY_RESET, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class BuildAESKey : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[20]; - } __packed; - - typedef Transaction<CommandID::NEW_AES_KEY, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - } -} - -/* -* STICK20 protocol command ids -* a superset of STICK10 -*/ -namespace stick20 { - namespace command { - class EnableEncryptedPartition : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; // TODO check w/ firmware - }; - - typedef Transaction<CommandID::ENABLE_CRYPTED_PARI, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class DisableEncryptedPartition : semantics::non_constructible { - public: - typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - class EnableHiddenEncryptedPartition : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; // TODO check w/ firmware - }; - - typedef Transaction<CommandID::ENABLE_HIDDEN_CRYPTED_PARI, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class DisableHiddenEncryptedPartition : semantics::non_constructible { - public: - typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - class EnableFirmwareUpdate : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; // TODO check w/ firmware - }; - - typedef Transaction<CommandID::ENABLE_FIRMWARE_UPDATE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class UpdatePassword : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t old_password[15]; - uint8_t new_password[15]; - }; - - typedef Transaction<CommandID::CHANGE_UPDATE_PIN, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class ExportFirmware : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::EXPORT_FIRMWARE_TO_FILE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class CreateNewKeys : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::GENERATE_NEW_KEYS, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class FillSDCardWithRandomChars : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t volume_flag; - uint8_t password[30]; - }; - - typedef Transaction<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SetupHiddenVolume : semantics::non_constructible { - public: - typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendPasswordMatrix : semantics::non_constructible { - public: - typedef Transaction<CommandID::SEND_PASSWORD_MATRIX, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendPasswordMatrixPinData : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t pin_data[30]; // TODO how long actually can it be? - }; - - typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_PINDATA, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendPasswordMatrixSetup : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t setup_data[30]; // TODO how long actually can it be? - }; - - typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_SETUP, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class GetDeviceStatus : semantics::non_constructible { - public: - typedef Transaction<CommandID::GET_DEVICE_STATUS, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendPassword : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::SEND_PASSWORD, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendNewPassword : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::SEND_NEW_PASSWORD, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - // TODO fix original nomenclature - class SendSetReadonlyToUncryptedVolume : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendSetReadwriteToUncryptedVolume : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendClearNewSdCardFound : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::CLEAR_NEW_SD_CARD_FOUND, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendStartup : semantics::non_constructible { - public: - struct CommandPayload { - uint64_t localtime; // POSIX - }; - - typedef Transaction<CommandID::SEND_STARTUP, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class SendHiddenVolumeSetup : semantics::non_constructible { - public: - struct CommandPayload { - // TODO HiddenVolumeSetup_tst type - }; - - typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class LockFirmware : semantics::non_constructible { - public: - struct CommandPayload { - uint8_t password[30]; - }; - - typedef Transaction<CommandID::SEND_LOCK_STICK_HARDWARE, - struct CommandPayload, - struct EmptyPayload> CommandTransaction; - }; - - class ProductionTest : semantics::non_constructible { - public: - typedef Transaction<CommandID::PRODUCTION_TEST, - struct EmptyPayload, - struct EmptyPayload> CommandTransaction; - }; - } } } - #endif diff --git a/include/dissect.h b/include/dissect.h new file mode 100644 index 0000000..993d348 --- /dev/null +++ b/include/dissect.h @@ -0,0 +1,82 @@ +/* + * Protocol packet dissection + */ +#ifndef DISSECT_H +#define DISSECT_H +#include <string> +#include <sstream> +#include "misc.h" +#include "cxx_semantics.h" +#include "command_id.h" +#include "device_proto.h" + +namespace nitrokey { +namespace proto { + +template <CommandID id, class HIDPacket> +class QueryDissector : semantics::non_constructible { + public: + static std::string dissect(const HIDPacket &pod) { + std::stringstream out; + + out << "Raw HID packet:" << std::endl; + out << ::nitrokey::misc::hexdump((const char *)(&pod), sizeof pod); + + out << "Contents:" << std::endl; + out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) + << std::endl; + out << "CRC:\t" << pod.crc << std::endl; + + out << "Payload:" << std::endl; + out << pod.payload.dissect(); + return out.str(); + } +}; + +template <CommandID id, class HIDPacket> +class ResponseDissector : semantics::non_constructible { + public: + static std::string dissect(const HIDPacket &pod) { + std::stringstream out; + + // FIXME use values from firmware (possibly generate separate + // header automatically) + std::string status[4]; + status[0] = " STATUS_READY"; + status[1] = " STATUS_BUSY"; + status[2] = " STATUS_ERROR"; + status[3] = " STATUS_RECEIVED_REPORT"; + std::string cmd[11]; + cmd[0] = " CMD_STATUS_OK"; + cmd[1] = " CMD_STATUS_WRONG_CRC"; + cmd[2] = " CMD_STATUS_WRONG_SLOT"; + cmd[3] = " CMD_STATUS_SLOT_NOT_PROGRAMMED"; + cmd[4] = " CMD_STATUS_WRONG_PASSWORD"; + cmd[5] = " CMD_STATUS_NOT_AUTHORIZED"; + cmd[6] = " CMD_STATUS_TIMESTAMP_WARNING"; + cmd[7] = " CMD_STATUS_NO_NAME_ERROR"; + cmd[8] = " CMD_STATUS_NOT_SUPPORTED"; + cmd[9] = " CMD_STATUS_UNKNOWN_COMMAND"; + cmd[10] = " CMD_STATUS_AES_DEC_FAILED"; + + out << "Raw HID packet:" << std::endl; + out << ::nitrokey::misc::hexdump((const char *)(&pod), sizeof pod); + + out << "Device status:\t" << pod.device_status + 0 << " " + << status[pod.device_status] << std::endl; + out << "Command ID:\t" << commandid_to_string((CommandID)(pod.command_id)) + << std::endl; + out << "Last command CRC:\t" << pod.last_command_crc << std::endl; + out << "Last command status:\t" << pod.last_command_status + 0 << " " + << cmd[pod.last_command_status] << std::endl; + out << "CRC:\t" << pod.crc << std::endl; + + out << "Payload:" << std::endl; + out << pod.payload.dissect(); + return out.str(); + } +}; +} +} + +#endif diff --git a/include/inttypes.h b/include/inttypes.h new file mode 100644 index 0000000..de2cc83 --- /dev/null +++ b/include/inttypes.h @@ -0,0 +1,522 @@ +/* Copyright (c) 2004,2005,2007 Joerg Wunsch Copyright (c) 2005, Carlos Lamas All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id: inttypes.h 1766 2008-10-17 21:33:57Z arcanum $ */ + +#ifndef __INTTYPES_H_ +#define __INTTYPES_H_ + +#include <stdint.h> + +/** \file */ +/** \defgroup avr_inttypes <inttypes.h>: Integer Type conversions + \code #include <inttypes.h> \endcode + + This header file includes the exact-width integer definitions from + <tt><stdint.h></tt>, and extends them with additional facilities + provided by the implementation. + + Currently, the extensions include two additional integer types + that could hold a "far" pointer (i.e. a code pointer that can + address more than 64 KB), as well as standard names for all printf + and scanf formatting options that are supported by the \ref avr_stdio. + As the library does not support the full range of conversion + specifiers from ISO 9899:1999, only those conversions that are + actually implemented will be listed here. + + The idea behind these conversion macros is that, for each of the + types defined by <stdint.h>, a macro will be supplied that portably + allows formatting an object of that type in printf() or scanf() + operations. Example: + + \code + #include <inttypes.h> + + uint8_t smallval; + int32_t longval; + ... + printf("The hexadecimal value of smallval is %" PRIx8 + ", the decimal value of longval is %" PRId32 ".\n", + smallval, longval); + \endcode +*/ + +/** \name Far pointers for memory access >64K */ + +/* @{ */ +/** \ingroup avr_inttypes + signed integer type that can hold a pointer > 64 KB */ +typedef int32_t int_farptr_t; + +/** \ingroup avr_inttypes + unsigned integer type that can hold a pointer > 64 KB */ +typedef uint32_t uint_farptr_t; + +/* @} */ + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + + +/** \name macros for printf and scanf format specifiers + + For C++, these are only included if __STDC_LIMIT_MACROS + is defined before including <inttypes.h>. + */ + +/* @{ */ +/** \ingroup avr_inttypes + decimal printf format for int8_t */ +#define PRId8 "d" +/** \ingroup avr_inttypes + decimal printf format for int_least8_t */ +#define PRIdLEAST8 "d" +/** \ingroup avr_inttypes + decimal printf format for int_fast8_t */ +#define PRIdFAST8 "d" + +/** \ingroup avr_inttypes + integer printf format for int8_t */ +#define PRIi8 "i" +/** \ingroup avr_inttypes + integer printf format for int_least8_t */ +#define PRIiLEAST8 "i" +/** \ingroup avr_inttypes + integer printf format for int_fast8_t */ +#define PRIiFAST8 "i" + + +/** \ingroup avr_inttypes + decimal printf format for int16_t */ +#define PRId16 "d" +/** \ingroup avr_inttypes + decimal printf format for int_least16_t */ +#define PRIdLEAST16 "d" +/** \ingroup avr_inttypes + decimal printf format for int_fast16_t */ +#define PRIdFAST16 "d" + +/** \ingroup avr_inttypes + integer printf format for int16_t */ +#define PRIi16 "i" +/** \ingroup avr_inttypes + integer printf format for int_least16_t */ +#define PRIiLEAST16 "i" +/** \ingroup avr_inttypes + integer printf format for int_fast16_t */ +#define PRIiFAST16 "i" + + +/** \ingroup avr_inttypes + decimal printf format for int32_t */ +#define PRId32 "ld" +/** \ingroup avr_inttypes + decimal printf format for int_least32_t */ +#define PRIdLEAST32 "ld" +/** \ingroup avr_inttypes + decimal printf format for int_fast32_t */ +#define PRIdFAST32 "ld" + +/** \ingroup avr_inttypes + integer printf format for int32_t */ +#define PRIi32 "li" +/** \ingroup avr_inttypes + integer printf format for int_least32_t */ +#define PRIiLEAST32 "li" +/** \ingroup avr_inttypes + integer printf format for int_fast32_t */ +#define PRIiFAST32 "li" + + +#ifdef __avr_libc_does_not_implement_long_long_in_printf_or_scanf + +#define PRId64 "lld" +#define PRIdLEAST64 "lld" +#define PRIdFAST64 "lld" + +#define PRIi64 "lli" +#define PRIiLEAST64 "lli" +#define PRIiFAST64 "lli" + + +#define PRIdMAX "lld" +#define PRIiMAX "lli" + +#endif + +/** \ingroup avr_inttypes + decimal printf format for intptr_t */ +#define PRIdPTR PRId16 +/** \ingroup avr_inttypes + integer printf format for intptr_t */ +#define PRIiPTR PRIi16 + +/** \ingroup avr_inttypes + octal printf format for uint8_t */ +#define PRIo8 "o" +/** \ingroup avr_inttypes + octal printf format for uint_least8_t */ +#define PRIoLEAST8 "o" +/** \ingroup avr_inttypes + octal printf format for uint_fast8_t */ +#define PRIoFAST8 "o" + +/** \ingroup avr_inttypes + decimal printf format for uint8_t */ +#define PRIu8 "u" +/** \ingroup avr_inttypes + decimal printf format for uint_least8_t */ +#define PRIuLEAST8 "u" +/** \ingroup avr_inttypes + decimal printf format for uint_fast8_t */ +#define PRIuFAST8 "u" + +/** \ingroup avr_inttypes + hexadecimal printf format for uint8_t */ +#define PRIx8 "x" +/** \ingroup avr_inttypes + hexadecimal printf format for uint_least8_t */ +#define PRIxLEAST8 "x" +/** \ingroup avr_inttypes + hexadecimal printf format for uint_fast8_t */ +#define PRIxFAST8 "x" + +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint8_t */ +#define PRIX8 "X" +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint_least8_t */ +#define PRIXLEAST8 "X" +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint_fast8_t */ +#define PRIXFAST8 "X" + + +/** \ingroup avr_inttypes + octal printf format for uint16_t */ +#define PRIo16 "o" +/** \ingroup avr_inttypes + octal printf format for uint_least16_t */ +#define PRIoLEAST16 "o" +/** \ingroup avr_inttypes + octal printf format for uint_fast16_t */ +#define PRIoFAST16 "o" + +/** \ingroup avr_inttypes + decimal printf format for uint16_t */ +#define PRIu16 "u" +/** \ingroup avr_inttypes + decimal printf format for uint_least16_t */ +#define PRIuLEAST16 "u" +/** \ingroup avr_inttypes + decimal printf format for uint_fast16_t */ +#define PRIuFAST16 "u" + +/** \ingroup avr_inttypes + hexadecimal printf format for uint16_t */ +#define PRIx16 "x" +/** \ingroup avr_inttypes + hexadecimal printf format for uint_least16_t */ +#define PRIxLEAST16 "x" +/** \ingroup avr_inttypes + hexadecimal printf format for uint_fast16_t */ +#define PRIxFAST16 "x" + +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint16_t */ +#define PRIX16 "X" +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint_least16_t */ +#define PRIXLEAST16 "X" +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint_fast16_t */ +#define PRIXFAST16 "X" + + +/** \ingroup avr_inttypes + octal printf format for uint32_t */ +#define PRIo32 "lo" +/** \ingroup avr_inttypes + octal printf format for uint_least32_t */ +#define PRIoLEAST32 "lo" +/** \ingroup avr_inttypes + octal printf format for uint_fast32_t */ +#define PRIoFAST32 "lo" + +/** \ingroup avr_inttypes + decimal printf format for uint32_t */ +#define PRIu32 "lu" +/** \ingroup avr_inttypes + decimal printf format for uint_least32_t */ +#define PRIuLEAST32 "lu" +/** \ingroup avr_inttypes + decimal printf format for uint_fast32_t */ +#define PRIuFAST32 "lu" + +/** \ingroup avr_inttypes + hexadecimal printf format for uint32_t */ +#define PRIx32 "lx" +/** \ingroup avr_inttypes + hexadecimal printf format for uint_least32_t */ +#define PRIxLEAST32 "lx" +/** \ingroup avr_inttypes + hexadecimal printf format for uint_fast32_t */ +#define PRIxFAST32 "lx" + +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint32_t */ +#define PRIX32 "lX" +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint_least32_t */ +#define PRIXLEAST32 "lX" +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uint_fast32_t */ +#define PRIXFAST32 "lX" + + +#ifdef __avr_libc_does_not_implement_long_long_in_printf_or_scanf + +#define PRIo64 "llo" +#define PRIoLEAST64 "llo" +#define PRIoFAST64 "llo" + +#define PRIu64 "llu" +#define PRIuLEAST64 "llu" +#define PRIuFAST64 "llu" + +#define PRIx64 "llx" +#define PRIxLEAST64 "llx" +#define PRIxFAST64 "llx" + +#define PRIX64 "llX" +#define PRIXLEAST64 "llX" +#define PRIXFAST64 "llX" + +#define PRIoMAX "llo" +#define PRIuMAX "llu" +#define PRIxMAX "llx" +#define PRIXMAX "llX" + +#endif + +/** \ingroup avr_inttypes + octal printf format for uintptr_t */ +#define PRIoPTR PRIo16 +/** \ingroup avr_inttypes + decimal printf format for uintptr_t */ +#define PRIuPTR PRIu16 +/** \ingroup avr_inttypes + hexadecimal printf format for uintptr_t */ +#define PRIxPTR PRIx16 +/** \ingroup avr_inttypes + uppercase hexadecimal printf format for uintptr_t */ +#define PRIXPTR PRIX16 + + +#ifdef __avr_libc_does_not_implement_hh_in_scanf + +#define SCNd8 "hhd" +#define SCNdLEAST8 "hhd" +#define SCNdFAST8 "hhd" + +#define SCNi8 "hhi" +#define SCNiLEAST8 "hhi" +#define SCNiFAST8 "hhi" + +#endif + + +/** \ingroup avr_inttypes + decimal scanf format for int16_t */ +#define SCNd16 "d" +/** \ingroup avr_inttypes + decimal scanf format for int_least16_t */ +#define SCNdLEAST16 "d" +/** \ingroup avr_inttypes + decimal scanf format for int_fast16_t */ +#define SCNdFAST16 "d" + +/** \ingroup avr_inttypes + generic-integer scanf format for int16_t */ +#define SCNi16 "i" +/** \ingroup avr_inttypes + generic-integer scanf format for int_least16_t */ +#define SCNiLEAST16 "i" +/** \ingroup avr_inttypes + generic-integer scanf format for int_fast16_t */ +#define SCNiFAST16 "i" + + +/** \ingroup avr_inttypes + decimal scanf format for int32_t */ +#define SCNd32 "ld" +/** \ingroup avr_inttypes + decimal scanf format for int_least32_t */ +#define SCNdLEAST32 "ld" +/** \ingroup avr_inttypes + decimal scanf format for int_fast32_t */ +#define SCNdFAST32 "ld" + +/** \ingroup avr_inttypes + generic-integer scanf format for int32_t */ +#define SCNi32 "li" +/** \ingroup avr_inttypes + generic-integer scanf format for int_least32_t */ +#define SCNiLEAST32 "li" +/** \ingroup avr_inttypes + generic-integer scanf format for int_fast32_t */ +#define SCNiFAST32 "li" + + +#ifdef __avr_libc_does_not_implement_long_long_in_printf_or_scanf + +#define SCNd64 "lld" +#define SCNdLEAST64 "lld" +#define SCNdFAST64 "lld" + +#define SCNi64 "lli" +#define SCNiLEAST64 "lli" +#define SCNiFAST64 "lli" + +#define SCNdMAX "lld" +#define SCNiMAX "lli" + +#endif + +/** \ingroup avr_inttypes + decimal scanf format for intptr_t */ +#define SCNdPTR SCNd16 +/** \ingroup avr_inttypes + generic-integer scanf format for intptr_t */ +#define SCNiPTR SCNi16 + +#ifdef __avr_libc_does_not_implement_hh_in_scanf + +#define SCNo8 "hho" +#define SCNoLEAST8 "hho" +#define SCNoFAST8 "hho" + +#define SCNu8 "hhu" +#define SCNuLEAST8 "hhu" +#define SCNuFAST8 "hhu" + +#define SCNx8 "hhx" +#define SCNxLEAST8 "hhx" +#define SCNxFAST8 "hhx" + +#endif + +/** \ingroup avr_inttypes + octal scanf format for uint16_t */ +#define SCNo16 "o" +/** \ingroup avr_inttypes + octal scanf format for uint_least16_t */ +#define SCNoLEAST16 "o" +/** \ingroup avr_inttypes + octal scanf format for uint_fast16_t */ +#define SCNoFAST16 "o" + +/** \ingroup avr_inttypes + decimal scanf format for uint16_t */ +#define SCNu16 "u" +/** \ingroup avr_inttypes + decimal scanf format for uint_least16_t */ +#define SCNuLEAST16 "u" +/** \ingroup avr_inttypes + decimal scanf format for uint_fast16_t */ +#define SCNuFAST16 "u" + +/** \ingroup avr_inttypes + hexadecimal scanf format for uint16_t */ +#define SCNx16 "x" +/** \ingroup avr_inttypes + hexadecimal scanf format for uint_least16_t */ +#define SCNxLEAST16 "x" +/** \ingroup avr_inttypes + hexadecimal scanf format for uint_fast16_t */ +#define SCNxFAST16 "x" + + +/** \ingroup avr_inttypes + octal scanf format for uint32_t */ +#define SCNo32 "lo" +/** \ingroup avr_inttypes + octal scanf format for uint_least32_t */ +#define SCNoLEAST32 "lo" +/** \ingroup avr_inttypes + octal scanf format for uint_fast32_t */ +#define SCNoFAST32 "lo" + +/** \ingroup avr_inttypes + decimal scanf format for uint32_t */ +#define SCNu32 "lu" +/** \ingroup avr_inttypes + decimal scanf format for uint_least32_t */ +#define SCNuLEAST32 "lu" +/** \ingroup avr_inttypes + decimal scanf format for uint_fast32_t */ +#define SCNuFAST32 "lu" + +/** \ingroup avr_inttypes + hexadecimal scanf format for uint32_t */ +#define SCNx32 "lx" +/** \ingroup avr_inttypes + hexadecimal scanf format for uint_least32_t */ +#define SCNxLEAST32 "lx" +/** \ingroup avr_inttypes + hexadecimal scanf format for uint_fast32_t */ +#define SCNxFAST32 "lx" + + +#ifdef __avr_libc_does_not_implement_long_long_in_printf_or_scanf + +#define SCNo64 "llo" +#define SCNoLEAST64 "llo" +#define SCNoFAST64 "llo" + +#define SCNu64 "llu" +#define SCNuLEAST64 "llu" +#define SCNuFAST64 "llu" + +#define SCNx64 "llx" +#define SCNxLEAST64 "llx" +#define SCNxFAST64 "llx" + +#define SCNoMAX "llo" +#define SCNuMAX "llu" +#define SCNxMAX "llx" + +#endif + +/** \ingroup avr_inttypes + octal scanf format for uintptr_t */ +#define SCNoPTR SCNo16 +/** \ingroup avr_inttypes + decimal scanf format for uintptr_t */ +#define SCNuPTR SCNu16 +/** \ingroup avr_inttypes + hexadecimal scanf format for uintptr_t */ +#define SCNxPTR SCNx16 + +/* @} */ + + +#endif /* !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) */ + + +#endif /* __INTTYPES_H_ */ diff --git a/include/log.h b/include/log.h new file mode 100644 index 0000000..8eda4fb --- /dev/null +++ b/include/log.h @@ -0,0 +1,50 @@ +#ifndef LOG_H +#define LOG_H +#include <string> +#include <cstddef> + +namespace nitrokey { +namespace log { + +enum class Loglevel : int { DEBUG_L2, DEBUG, INFO, WARNING, ERROR }; + +class LogHandler { + public: + virtual void print(const std::string &, Loglevel lvl) = 0; + + protected: + std::string loglevel_to_str(Loglevel); +}; + +class StdlogHandler : public LogHandler { + public: + virtual void print(const std::string &, Loglevel lvl); +}; + +extern StdlogHandler stdlog_handler; + +class Log { + public: + Log() : mp_loghandler(&stdlog_handler), m_loglevel(Loglevel::WARNING) {} + + static Log &instance() { + if (mp_instance == NULL) mp_instance = new Log; + return *mp_instance; + } + + void operator()(const std::string &, Loglevel); + + void set_loglevel(Loglevel lvl) { m_loglevel = lvl; } + + void set_handler(LogHandler *handler) { mp_loghandler = handler; } + + private: + Loglevel m_loglevel; + LogHandler *mp_loghandler; + + static Log *mp_instance; +}; +} +} + +#endif diff --git a/include/misc.h b/include/misc.h new file mode 100644 index 0000000..07f221c --- /dev/null +++ b/include/misc.h @@ -0,0 +1,14 @@ +#ifndef MISC_H +#define MISC_H +#include <stdio.h> +#include <string> + +namespace nitrokey { +namespace misc { + +std::string hexdump(const char *p, size_t size); +uint32_t stm_crc32(const uint8_t *data, size_t size); +} +} + +#endif diff --git a/include/stick10_commands.h b/include/stick10_commands.h new file mode 100644 index 0000000..f1903aa --- /dev/null +++ b/include/stick10_commands.h @@ -0,0 +1,588 @@ +#ifndef STICK10_COMMANDS_H +#define STICK10_COMMANDS_H +#include <string> +#include <sstream> +#include "inttypes.h" +#include "command.h" + +namespace nitrokey { +namespace proto { + +/* + * Stick10 protocol definition + */ +namespace stick10 { +class GetSlotName : public Command<CommandID::READ_SLOT_NAME> { + public: + // reachable as a typedef in Transaction + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + struct ResponsePayload { + uint8_t slot_name[15]; + + bool isValid() const { return true; } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, + struct ResponsePayload> CommandTransaction; +}; + +class EraseSlot : Command<CommandID::ERASE_SLOT> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class SetTime : Command<CommandID::SET_TIME> { + public: + struct CommandPayload { + uint8_t reset; // 0 - get time, 1 - set time + uint64_t time; // posix time + + bool isValid() const { return reset && reset != 1; } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +// TODO duplicate TOTP +class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[15]; + uint8_t slot_secret[20]; + uint8_t slot_config; + uint8_t slot_token_id[13]; + uint8_t slot_counter[8]; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + ss << "slot_name:\t" << slot_name << std::endl; + ss << "slot_secret:\t" << slot_secret << std::endl; + ss << "slot_config:\t" << slot_config << std::endl; + ss << "slot_token_id:\t" << slot_token_id << std::endl; + ss << "slot_counter:\t" << slot_counter << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class WriteToTOTPSlot : Command<CommandID::WRITE_TO_SLOT> { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[15]; + uint8_t slot_secret[20]; + uint8_t slot_config; + uint8_t slot_token_id[13]; + uint16_t slot_interval; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class GetCode : Command<CommandID::GET_CODE> { + public: + struct CommandPayload { + uint8_t slot_number; + uint64_t challenge; + uint64_t last_totp_time; + uint8_t last_interval; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + struct ResponsePayload { + uint8_t code[18]; + + bool isValid() const { return true; } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, + struct ResponsePayload> CommandTransaction; +}; + +class GetHOTP : Command<CommandID::GET_CODE> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return (slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + union { + uint8_t whole_response[18]; //TODO remove if not needed + struct { + uint32_t code; + uint8_t config; + } __packed; + } __packed; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << "code:\t" << (code) << std::endl; + ss << "config:\t" << "TODO" /*(config) */<< std::endl; //TODO show byte field options + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload> + CommandTransaction; +}; + +class ReadSlot : Command<CommandID::READ_SLOT> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + + std::string dissect() const { + std::stringstream ss; + ss << "slot_number:\t" << (int)(slot_number) << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_name[15]; + uint8_t config; + uint8_t token_id[13]; + uint64_t counter; + + 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; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, + struct ResponsePayload> CommandTransaction; +}; + +class GetStatus : Command<CommandID::GET_STATUS> { + public: + struct ResponsePayload { + uint16_t firmware_version; + uint8_t card_serial[4]; + uint8_t general_config[3]; + uint8_t otp_password_config[2]; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + ss << "firmware_version:\t" << firmware_version << std::endl; + ss << "card_serial:\t" + << ::nitrokey::misc::hexdump((const char *)(card_serial), + sizeof card_serial); + ss << "general_config:\t" + << ::nitrokey::misc::hexdump((const char *)(general_config), + sizeof general_config); + ss << "otp_password_config:\t" + << ::nitrokey::misc::hexdump((const char *)(otp_password_config), + sizeof otp_password_config); + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload> + CommandTransaction; +}; + +class GetPasswordRetryCount : Command<CommandID::GET_PASSWORD_RETRY_COUNT> { + public: + struct ResponsePayload { + uint8_t password_retry_count; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " password_retry_count\t" << password_retry_count << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload> + CommandTransaction; +}; + +class GetUserPasswordRetryCount + : Command<CommandID::GET_USER_PASSWORD_RETRY_COUNT> { + public: + struct ResponsePayload { + uint8_t password_retry_count; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " password_retry_count\t" << password_retry_count << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload> + CommandTransaction; +}; + +class GetPasswordSafeSlotStatus : Command<CommandID::GET_PW_SAFE_SLOT_STATUS> { + public: + struct ResponsePayload { + uint8_t password_safe_status[PWS_SLOT_COUNT]; + + bool isValid() const { return true; } + } __packed; + + typedef Transaction<command_id(), struct EmptyPayload, struct ResponsePayload> + CommandTransaction; +}; + +class GetPasswordSafeSlotName : Command<CommandID::GET_PW_SAFE_SLOT_NAME> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << "slot_number\t" << slot_number << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_name[PWS_SLOTNAME_LENGTH]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " slot_name\t" << slot_name << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, + struct ResponsePayload> CommandTransaction; +}; + +class GetPasswordSafeSlotPassword + : Command<CommandID::GET_PW_SAFE_SLOT_PASSWORD> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << slot_number << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_password[PWS_PASSWORD_LENGTH]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " slot_password\t" << slot_password << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, + struct ResponsePayload> CommandTransaction; +}; + +class GetPasswordSafeSlotLogin + : Command<CommandID::GET_PW_SAFE_SLOT_LOGINNAME> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + std::string dissect() const { + std::stringstream ss; + ss << " slot_number\t" << slot_number << std::endl; + return ss.str(); + } + } __packed; + + struct ResponsePayload { + uint8_t slot_login[PWS_LOGINNAME_LENGTH]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " slot_login\t" << slot_login << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, + struct ResponsePayload> CommandTransaction; +}; + +class SetPasswordSafeSlotData : Command<CommandID::SET_PW_SAFE_SLOT_DATA_1> { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[PWS_SLOTNAME_LENGTH]; + uint8_t slot_password[PWS_PASSWORD_LENGTH]; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class SetPasswordSafeSlotData2 : Command<CommandID::SET_PW_SAFE_SLOT_DATA_2> { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_name[PWS_SLOTNAME_LENGTH]; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class ErasePasswordSafeSlot : Command<CommandID::PW_SAFE_ERASE_SLOT> { + public: + struct CommandPayload { + uint8_t slot_number; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class EnablePasswordSafe : Command<CommandID::PW_SAFE_ENABLE> { + public: + struct CommandPayload { + uint8_t password[30]; + + bool isValid() const { return true; } + std::string dissect() const { + std::stringstream ss; + ss << " password\t" << password << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class PasswordSafeInitKey : Command<CommandID::PW_SAFE_INIT_KEY> { + public: + typedef Transaction<command_id(), struct EmptyPayload, struct EmptyPayload> + CommandTransaction; +}; + +// TODO naming screwed up, see above +class PasswordSafeSendSlotViaHID : Command<CommandID::PW_SAFE_SEND_DATA> { + public: + struct CommandPayload { + uint8_t slot_number; + uint8_t slot_kind; + + bool isValid() const { return !(slot_number & 0xF0); } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +// TODO "Device::passwordSafeSendSlotDataViaHID" + +class WriteGeneralConfig : Command<CommandID::WRITE_CONFIG> { + public: + struct CommandPayload { + uint8_t config[5]; + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class FirstAuthenticate : Command<CommandID::FIRST_AUTHENTICATE> { + public: + struct CommandPayload { + uint8_t card_password[25]; + uint8_t temporary_password[25]; + + bool isValid() const { return true; } + + std::string dissect() const { + std::stringstream ss; + ss << "card_password:\t" << card_password << std::endl; + ss << "temporary_password:\t" << temporary_password << std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class UserAuthenticate : Command<CommandID::USER_AUTHENTICATE> { + public: + struct CommandPayload { + uint8_t card_password[25]; + uint8_t temporary_password[25]; + + bool isValid() const { return true; } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class Authorize : Command<CommandID::AUTHORIZE> { + public: + struct CommandPayload { + uint32_t crc_to_authorize; + uint8_t temporary_password[25]; + + std::string dissect() const { + std::stringstream ss; + ss << " crc_to_authorize:\t" << crc_to_authorize<< std::endl; + ss << " temporary_password:\t" << temporary_password<< std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class UserAuthorize : Command<CommandID::USER_AUTHORIZE> { + public: + struct CommandPayload { + uint8_t crc_to_authorize[4]; + uint8_t temporary_password[25]; + std::string dissect() const { + std::stringstream ss; + ss << " crc_to_authorize:\t" << crc_to_authorize<< std::endl; + ss << " temporary_password:\t" << temporary_password<< std::endl; + return ss.str(); + } + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class UnlockUserPassword : Command<CommandID::UNLOCK_USER_PASSWORD> { + public: + struct CommandPayload { + uint8_t admin_password[20]; // TODO + } __packed; + + // TODO could we get the stick to return the retry count? + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class ChangeUserPin : Command<CommandID::CHANGE_USER_PIN> { + public: + struct CommandPayload { + uint8_t old_pin[25]; + uint8_t new_pin[25]; + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +// TODO why is it needed? +class IsAESSupported : Command<CommandID::DETECT_SC_AES> { + public: + struct CommandPayload { + uint8_t password[20]; + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class ChangeAdminPin : Command<CommandID::CHANGE_ADMIN_PIN> { + public: + struct CommandPayload { + uint8_t old_pin[25]; + uint8_t new_pin[25]; + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class LockDevice : Command<CommandID::LOCK_DEVICE> { + public: + typedef Transaction<command_id(), struct EmptyPayload, struct EmptyPayload> + CommandTransaction; +}; + +class FactoryReset : Command<CommandID::FACTORY_RESET> { + public: + struct CommandPayload { + uint8_t password[20]; + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; + +class BuildAESKey : Command<CommandID::NEW_AES_KEY> { + public: + struct CommandPayload { + uint8_t password[20]; + } __packed; + + typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload> + CommandTransaction; +}; +} +} +} +#endif diff --git a/include/stick20_commands.h b/include/stick20_commands.h new file mode 100644 index 0000000..b84d436 --- /dev/null +++ b/include/stick20_commands.h @@ -0,0 +1,233 @@ +#ifndef STICK20_COMMANDS_H +#define STICK20_COMMANDS_H +#include "inttypes.h" + +namespace nitrokey { +namespace proto { + +/* +* STICK20 protocol command ids +* a superset of STICK10 +*/ +namespace stick20 { +class EnableEncryptedPartition : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; // TODO check w/ firmware + }; + + typedef Transaction<CommandID::ENABLE_CRYPTED_PARI, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class DisableEncryptedPartition : semantics::non_constructible { + public: + typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, struct EmptyPayload, + struct EmptyPayload> CommandTransaction; +}; + +class EnableHiddenEncryptedPartition : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; // TODO check w/ firmware + }; + + typedef Transaction<CommandID::ENABLE_HIDDEN_CRYPTED_PARI, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class DisableHiddenEncryptedPartition : semantics::non_constructible { + public: + typedef Transaction<CommandID::DISABLE_CRYPTED_PARI, struct EmptyPayload, + struct EmptyPayload> CommandTransaction; +}; + +class EnableFirmwareUpdate : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; // TODO check w/ firmware + }; + + typedef Transaction<CommandID::ENABLE_FIRMWARE_UPDATE, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class UpdatePassword : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t old_password[15]; + uint8_t new_password[15]; + }; + + typedef Transaction<CommandID::CHANGE_UPDATE_PIN, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class ExportFirmware : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::EXPORT_FIRMWARE_TO_FILE, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class CreateNewKeys : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::GENERATE_NEW_KEYS, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class FillSDCardWithRandomChars : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t volume_flag; + uint8_t password[30]; + }; + + typedef Transaction<CommandID::FILL_SD_CARD_WITH_RANDOM_CHARS, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SetupHiddenVolume : semantics::non_constructible { + public: + typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP, struct EmptyPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendPasswordMatrix : semantics::non_constructible { + public: + typedef Transaction<CommandID::SEND_PASSWORD_MATRIX, struct EmptyPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendPasswordMatrixPinData : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t pin_data[30]; // TODO how long actually can it be? + }; + + typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_PINDATA, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendPasswordMatrixSetup : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t setup_data[30]; // TODO how long actually can it be? + }; + + typedef Transaction<CommandID::SEND_PASSWORD_MATRIX_SETUP, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class GetDeviceStatus : semantics::non_constructible { + public: + typedef Transaction<CommandID::GET_DEVICE_STATUS, struct EmptyPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendPassword : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::SEND_PASSWORD, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendNewPassword : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::SEND_NEW_PASSWORD, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +// TODO fix original nomenclature +class SendSetReadonlyToUncryptedVolume : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendSetReadwriteToUncryptedVolume : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::ENABLE_READWRITE_UNCRYPTED_LUN, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendClearNewSdCardFound : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::CLEAR_NEW_SD_CARD_FOUND, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendStartup : semantics::non_constructible { + public: + struct CommandPayload { + uint64_t localtime; // POSIX + }; + + typedef Transaction<CommandID::SEND_STARTUP, struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class SendHiddenVolumeSetup : semantics::non_constructible { + public: + struct CommandPayload { + // TODO HiddenVolumeSetup_tst type + }; + + typedef Transaction<CommandID::SEND_HIDDEN_VOLUME_SETUP, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class LockFirmware : semantics::non_constructible { + public: + struct CommandPayload { + uint8_t password[30]; + }; + + typedef Transaction<CommandID::SEND_LOCK_STICK_HARDWARE, + struct CommandPayload, + struct EmptyPayload> CommandTransaction; +}; + +class ProductionTest : semantics::non_constructible { + public: + typedef Transaction<CommandID::PRODUCTION_TEST, struct EmptyPayload, + struct EmptyPayload> CommandTransaction; +}; +} +} +} + +#endif @@ -0,0 +1,42 @@ +#include <iostream> +#include <string> +#include <ctime> +#include <iomanip> +#include "log.h" + +namespace nitrokey { +namespace log { + +Log *Log::mp_instance = NULL; +StdlogHandler stdlog_handler; + +std::string LogHandler::loglevel_to_str(Loglevel lvl) { + switch (lvl) { + case Loglevel::DEBUG_L2: + return std::string("DEBUG_L2"); + case Loglevel::DEBUG: + return std::string("DEBUG"); + case Loglevel::INFO: + return std::string("INFO"); + case Loglevel::WARNING: + return std::string("WARNING"); + case Loglevel::ERROR: + return std::string("ERROR"); + } + return std::string(""); +} + +void Log::operator()(const std::string &logstr, Loglevel lvl) { + if (mp_loghandler != NULL) + if ((int)(lvl) >= (int)(m_loglevel)) mp_loghandler->print(logstr, lvl); +} + +void StdlogHandler::print(const std::string &str, Loglevel lvl) { + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + + std::clog << "[" << loglevel_to_str(lvl) << "] [" + << std::put_time(&tm, "%c %Z") << "]\t" << str << std::endl; +} +} +} @@ -0,0 +1,49 @@ +#include <sstream> +#include <string> +#include "misc.h" +#include "inttypes.h" + +namespace nitrokey { +namespace misc { + +std::string hexdump(const char *p, size_t size) { + std::stringstream out; + char formatbuf[128]; + const char *pstart = p; + + for (const char *pend = p + size; p < pend;) { + snprintf(formatbuf, 128, "%04x\t", p - pstart); + out << formatbuf; + + for (const char *le = p + 16; p < le && p < pend; p++) { + snprintf(formatbuf, 128, "%02x ", uint8_t(*p)); + out << formatbuf; + } + out << std::endl; + } + return out.str(); +} + +static uint32_t _crc32(uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04C11DB7; // polynomial used in STM32 + else + crc = (crc << 1); + } + + return crc; +} + +uint32_t stm_crc32(const uint8_t *data, size_t size) { + uint32_t crc = 0xffffffff; + const uint32_t *pend = (const uint32_t *)(data + size); + for (const uint32_t *p = (const uint32_t *)(data); p < pend; p++) + crc = _crc32(crc, *p); + return crc; +} +} +} diff --git a/test.cc b/test.cc deleted file mode 100644 index 5c59c60..0000000 --- a/test.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include <iostream> -#include <string.h> -#include "device_proto.h" -#include "device.h" - -using namespace std; - -using namespace device; -using namespace proto; -using namespace proto::stick10::command; - -int main() { - Stick20 stick; - cout << stick.connect() << endl; - - { - auto resp = GetStatus::CommandTransaction::run(stick); - cout << resp.firmware_version << endl; - } - - { - FirstAuthenticate::CommandTransaction::CommandPayload authreq; - strcpy((char *)(authreq.card_password), "12345678"); - FirstAuthenticate::CommandTransaction::run(stick, authreq); - } - - { - for (int i=0; i<32; i++) { - GetSlotName::CommandTransaction::CommandPayload slotname_req; - slotname_req.slot_number=i; - auto slot_resp = GetSlotName::CommandTransaction::run(stick); - cout << slot_resp.slot_name << endl; - } - } -} diff --git a/unittest/Catch b/unittest/Catch new file mode 160000 +Subproject ae5ee2cf63d6d67bd1369b512d2a7b60b571c90 diff --git a/unittest/Makefile b/unittest/Makefile new file mode 100644 index 0000000..aa9d53a --- /dev/null +++ b/unittest/Makefile @@ -0,0 +1,33 @@ +CC = $(PREFIX)-gcc +#CXX = $(PREFIX)-g++ +CXX = clang++-3.6 +LD = $(CXX) + +INCLUDE = -I../include -ICatch/single_include/ +LIB = -L../build +LDLIBS = -lnitrokey +BUILD = build + +CXXFLAGS = -std=c++14 -fPIC + +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/build/libnitrokey.so b/unittest/build/libnitrokey.so new file mode 120000 index 0000000..69a5e22 --- /dev/null +++ b/unittest/build/libnitrokey.so @@ -0,0 +1 @@ +../../build/libnitrokey.so
\ No newline at end of file diff --git a/unittest/test.cc b/unittest/test.cc new file mode 100644 index 0000000..02f742c --- /dev/null +++ b/unittest/test.cc @@ -0,0 +1,80 @@ +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() +#include "catch.hpp" + +#include <iostream> +#include <string.h> +#include "device_proto.h" +#include "log.h" +#include "stick10_commands.h" + +using namespace std; +using namespace nitrokey::device; +using namespace nitrokey::proto::stick10; +using namespace nitrokey::log; + +std::string getSlotName(Stick10 &stick, int slotNo) { + ReadSlot::CommandTransaction::CommandPayload slot_req; + slot_req.slot_number = slotNo; + auto slot = ReadSlot::CommandTransaction::run(stick, slot_req); + std::string sName(reinterpret_cast<char *>(slot.slot_name)); + return sName; +} + +TEST_CASE("Slot names are correct", "[slotNames]") { + Stick10 stick; + bool connected = stick.connect(); + REQUIRE(connected == true); + + Log::instance().set_loglevel(Loglevel::DEBUG); + + auto resp = GetStatus::CommandTransaction::run(stick); + + FirstAuthenticate::CommandTransaction::CommandPayload authreq; + strcpy((char *)(authreq.card_password), "12345678"); + FirstAuthenticate::CommandTransaction::run(stick, authreq); + + { + EnablePasswordSafe::CommandTransaction::CommandPayload authreq; + strcpy((char *)(authreq.password), "123456"); + EnablePasswordSafe::CommandTransaction::run(stick, authreq); + } + + REQUIRE(getSlotName(stick, 0x20) == std::string("1")); + REQUIRE(getSlotName(stick, 0x21) == std::string("slot2")); + + { + auto resp = GetPasswordRetryCount::CommandTransaction::run(stick); + REQUIRE(resp.password_retry_count == 3); + } + { + auto resp = GetUserPasswordRetryCount::CommandTransaction::run(stick); + REQUIRE(resp.password_retry_count == 3); + } + + { + GetPasswordSafeSlotName::CommandTransaction::CommandPayload slot; + slot.slot_number = 0; + auto resp2 = GetPasswordSafeSlotName::CommandTransaction::run(stick, slot); + std::string sName(reinterpret_cast<char *>(resp2.slot_name)); + REQUIRE(sName == std::string("web1")); + } + + { + GetPasswordSafeSlotPassword::CommandTransaction::CommandPayload slot; + slot.slot_number = 0; + auto resp2 = + GetPasswordSafeSlotPassword::CommandTransaction::run(stick, slot); + std::string sName(reinterpret_cast<char *>(resp2.slot_password)); + REQUIRE(sName == std::string("pass1")); + } + + { + GetPasswordSafeSlotLogin::CommandTransaction::CommandPayload slot; + slot.slot_number = 0; + auto resp2 = GetPasswordSafeSlotLogin::CommandTransaction::run(stick, slot); + std::string sName(reinterpret_cast<char *>(resp2.slot_login)); + REQUIRE(sName == std::string("login1")); + } + + stick.disconnect(); +} diff --git a/unittest/test_HOTP.cc b/unittest/test_HOTP.cc new file mode 100644 index 0000000..b263693 --- /dev/null +++ b/unittest/test_HOTP.cc @@ -0,0 +1,101 @@ +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() +#include "catch.hpp" + +#include <iostream> +//#include <string.h> +#include "device_proto.h" +#include "log.h" +#include "stick10_commands.h" +#include <cstdlib> + +using namespace std; +using namespace nitrokey::device; +using namespace nitrokey::proto::stick10; +using namespace nitrokey::log; + +void hexStringToByte(uint8_t data[], const char* hexString){ + assert(strlen(hexString)%2==0); + char buf[2]; + for(int i=0; i<strlen(hexString); i++){ + buf[i%2] = hexString[i]; + if (i%2==1){ + data[i/2] = strtoul(buf, NULL, 16) & 0xFF; + } + } +}; + +TEST_CASE("test secret", "[functions]") { + uint8_t slot_secret[21]; + slot_secret[20] = 0; + const char* secretHex = "3132333435363738393031323334353637383930"; + hexStringToByte(slot_secret, secretHex); + CAPTURE(slot_secret); + REQUIRE(strcmp("12345678901234567890",reinterpret_cast<char *>(slot_secret) ) == 0 ); +} + +TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") { + Stick10 stick; + bool connected = stick.connect(); + + REQUIRE(connected == true); + + Log::instance().set_loglevel(Loglevel::DEBUG); + + auto resp = GetStatus::CommandTransaction::run(stick); + + const char * temporary_password = "123456789012345678901234"; + { + FirstAuthenticate::CommandTransaction::CommandPayload authreq; + strcpy((char *)(authreq.card_password), "12345678"); + strcpy((char *)(authreq.temporary_password), temporary_password); + FirstAuthenticate::CommandTransaction::run(stick, authreq); + } + + //test according to https://tools.ietf.org/html/rfc4226#page-32 + { + WriteToHOTPSlot::CommandTransaction::CommandPayload hwrite; + hwrite.slot_number = 0x10; + strcpy(reinterpret_cast<char *>(hwrite.slot_name), "rfc4226_lib"); + //strcpy(reinterpret_cast<char *>(hwrite.slot_secret), ""); + const char* secretHex = "3132333435363738393031323334353637383930"; + hexStringToByte(hwrite.slot_secret, secretHex); + //hwrite.slot_config; //TODO check various configs in separate test cases + //strcpy(reinterpret_cast<char *>(hwrite.slot_token_id), ""); + //strcpy(reinterpret_cast<char *>(hwrite.slot_counter), ""); + + //authorize writehotp first + { + Authorize::CommandTransaction::CommandPayload auth; + strcpy((char *)(auth.temporary_password), temporary_password); + auth.crc_to_authorize = WriteToHOTPSlot::CommandTransaction::getCRC(hwrite); + Authorize::CommandTransaction::run(stick, auth); + } + + //run hotp command + WriteToHOTPSlot::CommandTransaction::run(stick, hwrite); + + uint32_t codes[] = { + 755224, 287082, 359152, 969429, 338314, + 254676, 287922, 162583, 399871, 520489 + }; + + for( auto code: codes){ + GetHOTP::CommandTransaction::CommandPayload gh; + gh.slot_number = 0x10; + auto resp = GetHOTP::CommandTransaction::run(stick, gh); + REQUIRE( resp.code == code); + } + //checking slot programmed before with nitro-app + /* + for( auto code: codes){ + GetHOTP::CommandTransaction::CommandPayload gh; + gh.slot_number = 0x12; + auto resp = GetHOTP::CommandTransaction::run(stick, gh); + REQUIRE( resp.code == code); + } + */ + } + + + stick.disconnect(); +} |