aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Zalega <mateusz@appliedsourcery.com>2015-10-22 22:41:18 +0200
committerMateusz Zalega <mateusz@appliedsourcery.com>2015-10-22 22:41:18 +0200
commitc4aec144256e3f27fedd8f8de03e10cc08eecab8 (patch)
treee7dda787a0f98be420499a3c60f7a821d75ad60c
downloadlibnitrokey-c4aec144256e3f27fedd8f8de03e10cc08eecab8.tar.gz
libnitrokey-c4aec144256e3f27fedd8f8de03e10cc08eecab8.tar.bz2
Initial
-rw-r--r--Makefile31
-rw-r--r--crc32.cc37
-rw-r--r--device.cc52
-rw-r--r--include/crc32.h32
-rw-r--r--include/cxx_semantics.h15
-rw-r--r--include/device.h65
-rw-r--r--include/device_proto.h1055
-rw-r--r--test.cc35
8 files changed, 1322 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e98e631
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+CC = $(PREFIX)-gcc
+CXX = $(PREFIX)-g++
+LD = $(CXX)
+
+INCLUDE = -Iinclude/
+LIB = -lhidapi-libusb
+BUILD = build
+
+CXXFLAGS = -std=c++14
+
+CXXSOURCES = $(wildcard *.cc)
+OBJ = $(CXXSOURCES:%.cc:$(BUILD)/%.o)
+
+$(BUILD)/libnitrokey.so: $(OBJ)
+ $(CXX) -shared $(OBJ) -o $@
+
+$(BUILD)/%.o: %.cc
+ $(CXX) -c $< -o $@ $(INCLUDE) $(CXXFLAGS)
+
+all: $(OBJ) $(BUILD)/libnitrokey.so
+
+clean:
+ rm $(OBJ)
+ rm $(BUILD)/libnitrokey.so
+
+mrproper: clean
+ rm $(BUILD)/*.d
+
+.PHONY: all clean mrproper
+
+include $(wildcard build/*.d)
diff --git a/crc32.cc b/crc32.cc
new file mode 100644
index 0000000..558d4ef
--- /dev/null
+++ b/crc32.cc
@@ -0,0 +1,37 @@
+/*
+ * 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);
+}
diff --git a/device.cc b/device.cc
new file mode 100644
index 0000000..c954cb7
--- /dev/null
+++ b/device.cc
@@ -0,0 +1,52 @@
+#include <chrono>
+#include <thread>
+#include <cstddef>
+#include <hidapi/hidapi.h>
+#include "device.h"
+
+using namespace device;
+
+Device::Device()
+: m_vid(0), m_pid(0),
+ m_retry_count(4),
+ m_retry_timeout(50),
+ mp_devhandle(NULL) {}
+
+bool Device::connect() {
+ hid_init();
+
+ mp_devhandle = hid_open(m_vid, m_pid, NULL);
+ 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));
+}
+
+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;
+}
+
+Stick10::Stick10() {
+ m_vid = 0x20a0;
+ m_pid = 0x4108;
+}
+
+Stick20::Stick20() {
+ m_vid = 0x20a0;
+ m_pid = 0x4109;
+}
diff --git a/include/crc32.h b/include/crc32.h
new file mode 100644
index 0000000..b0e16ff
--- /dev/null
+++ b/include/crc32.h
@@ -0,0 +1,32 @@
+/*
+ * 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
new file mode 100644
index 0000000..f5d3dca
--- /dev/null
+++ b/include/cxx_semantics.h
@@ -0,0 +1,15 @@
+#ifndef CXX_SEMANTICS_H
+#define CXX_SEMANTICS_H
+
+#define __packed __attribute__((__packed__))
+
+/*
+ * There's no need to include Boost for a simple subset this project needs.
+ */
+namespace semantics {
+ class non_constructible {
+ non_constructible() {}
+ };
+}
+
+#endif
diff --git a/include/device.h b/include/device.h
new file mode 100644
index 0000000..b2d83b5
--- /dev/null
+++ b/include/device.h
@@ -0,0 +1,65 @@
+#ifndef DEVICE_H
+#define DEVICE_H
+#include <chrono>
+#include <hidapi/hidapi.h>
+#include "inttypes.h"
+
+#define HID_REPORT_SIZE 65
+
+// TODO !! SEMAPHORE
+
+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
+};
+
+class Device {
+public:
+ Device();
+
+ // 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);
+
+ /*
+ * Gets packet of HID_REPORT_SIZE.
+ * Can sleep. See below.
+ */
+ virtual CommError recv(void *packet);
+
+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;
+
+ hid_device *mp_devhandle;
+};
+
+class Stick10 : public Device {
+public:
+ Stick10();
+};
+
+class Stick20 : public Device {
+public:
+ Stick20();
+};
+}
+#endif
diff --git a/include/device_proto.h b/include/device_proto.h
new file mode 100644
index 0000000..fb1dd4e
--- /dev/null
+++ b/include/device_proto.h
@@ -0,0 +1,1055 @@
+#ifndef DEVICE_PROTO_H
+#define DEVICE_PROTO_H
+#include <utility>
+#include <type_traits>
+#include <stdexcept>
+#include <string>
+#include <strings.h>
+// a local version for compatibility with Windows
+#include "inttypes.h"
+#include "cxx_semantics.h"
+#include "device.h"
+#include "utils/crc32.h"
+
+#define STICK20_UPDATE_MODE_VID 0x03EB
+#define STICK20_UPDATE_MODE_PID 0x2FF1
+
+#define PAYLOAD_SIZE 53
+#define PWS_SLOT_COUNT 16
+#define PWS_SLOTNAME_LENGTH 11
+#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;
+}
+
+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.
+ *
+ * TODO (future) support for Big Endian
+ */
+
+/*
+ * Every packet is a USB HID report (check USB spec)
+ */
+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();
+ }
+} __packed;
+
+/*
+ * Response payload (the parametrized type inside struct HIDReport)
+ *
+ * command_id member in incoming HIDReport structure carries the command
+ * type last used.
+ */
+template <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();
+ }
+} __packed;
+
+struct EmptyPayload {
+ uint8_t _data[];
+
+ bool isValid() const {
+ return true;
+ }
+} __packed;
+
+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);
+ }
+};
+
+/*
+ * 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/test.cc b/test.cc
new file mode 100644
index 0000000..5c59c60
--- /dev/null
+++ b/test.cc
@@ -0,0 +1,35 @@
+#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;
+ }
+ }
+}