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(); +} | 
