aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzczepan Zalega <szczepan@nitrokey.com>2016-07-18 18:04:34 +0200
committerSzczepan Zalega <szczepan@nitrokey.com>2016-08-01 13:54:45 +0200
commita5d11eab6003a6ed0f7c78ecb7136b28ee938a23 (patch)
treefa1c5a1a476f51161b822188508704c5acd52a1d
parent209672f9bba667ea0fc7bce364ef0ad1c8bc4d2a (diff)
downloadlibnitrokey-a5d11eab6003a6ed0f7c78ecb7136b28ee938a23.tar.gz
libnitrokey-a5d11eab6003a6ed0f7c78ecb7136b28ee938a23.tar.bz2
Initial version of C/Python bindings
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
-rw-r--r--.gitmodules3
-rw-r--r--.idea/codeStyleSettings.xml38
-rw-r--r--.idea/dictionaries/sz.xml8
-rw-r--r--.idea/vcs.xml6
-rw-r--r--CMakeLists.txt27
-rw-r--r--NK_C_API.cc69
-rw-r--r--NK_C_API.h25
-rw-r--r--NitrokeyManager.cc151
-rw-r--r--build/test.py57
-rw-r--r--device.cc6
-rw-r--r--include/NitrokeyManager.h56
-rw-r--r--include/dissect.h4
-rw-r--r--include/stick10_commands.h91
-rw-r--r--python_bindings/api.cpp77
m---------python_bindings/pybind110
-rwxr-xr-xunittest/build/run.sh1
-rw-r--r--unittest/test_HOTP.cc14
17 files changed, 613 insertions, 20 deletions
diff --git a/.gitmodules b/.gitmodules
index c811cb7..dd13427 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "unittest/Catch"]
path = unittest/Catch
url = https://github.com/philsquared/Catch.git
+[submodule "pybind11"]
+ path = python_bindings/pybind11
+ url = https://github.com/pybind/pybind11.git
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100644
index 0000000..23f6cae
--- /dev/null
+++ b/.idea/codeStyleSettings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectCodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS">
+ <value>
+ <Objective-C-extensions>
+ <option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
+ <option name="RELEASE_STYLE" value="IVAR" />
+ <option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
+ <file>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
+ </file>
+ <class>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
+ </class>
+ <extensions>
+ <pair source="cpp" header="h" />
+ <pair source="c" header="h" />
+ </extensions>
+ </Objective-C-extensions>
+ </value>
+ </option>
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/dictionaries/sz.xml b/.idea/dictionaries/sz.xml
new file mode 100644
index 0000000..59b56fb
--- /dev/null
+++ b/.idea/dictionaries/sz.xml
@@ -0,0 +1,8 @@
+<component name="ProjectDictionaryState">
+ <dictionary name="sz">
+ <words>
+ <w>nitrokey</w>
+ <w>totp</w>
+ </words>
+ </dictionary>
+</component> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project> \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..62e8f15
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.5)
+project(libnitrokey)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+
+set(SOURCE_FILES
+ include/command.h
+ include/command_id.h
+ include/cxx_semantics.h
+ include/device.h
+ include/device_proto.h
+ include/dissect.h
+ include/inttypes.h
+ include/log.h
+ include/misc.h
+ include/NitrokeyManager.h
+ include/stick10_commands.h
+ include/stick20_commands.h
+ NK_C_API.h
+ command_id.cc
+ device.cc
+ log.cc
+ misc.cc
+ NitrokeyManager.cc
+ NK_C_API.cc)
+
+add_executable(libnitrokey ${SOURCE_FILES}) \ No newline at end of file
diff --git a/NK_C_API.cc b/NK_C_API.cc
new file mode 100644
index 0000000..cc35794
--- /dev/null
+++ b/NK_C_API.cc
@@ -0,0 +1,69 @@
+#include <cstring>
+#include "NK_C_API.h"
+
+using namespace nitrokey;
+
+extern "C"
+{
+extern int NK_login(const char *pin, const char *temporary_password) {
+ auto m = NitrokeyManager::instance();
+ return m->connect() && m->authorize(pin, temporary_password);
+}
+
+extern int NK_logout() {
+ auto m = NitrokeyManager::instance();
+ return m->disconnect();
+}
+extern const char * NK_status() {
+ auto m = NitrokeyManager::instance();
+ string s = m->get_status();
+ return strdup(s.c_str());
+}
+
+extern uint32_t NK_get_hotp_code(uint8_t slot_number){
+ auto m = NitrokeyManager::instance();
+ return m->get_HOTP_code(slot_number);
+}
+
+extern uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
+ uint8_t last_interval){
+ auto m = NitrokeyManager::instance();
+ return m->get_TOTP_code(slot_number, 0, 0, 0);
+}
+
+extern int NK_erase_hotp_slot(uint8_t slot_number) {
+ auto m = NitrokeyManager::instance();
+ return m->erase_hotp_slot(slot_number);
+}
+extern int NK_erase_totp_slot(uint8_t slot_number) {
+ auto m = NitrokeyManager::instance();
+ return m->erase_totp_slot(slot_number);
+}
+
+extern int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint8_t hotp_counter,
+ const char *temporary_password) {
+ auto m = NitrokeyManager::instance();
+ return m->write_HOTP_slot(slot_number, slot_name, secret, hotp_counter, temporary_password);
+}
+
+extern int NK_write_totp_slot(uint8_t slot_number, const char *secret, uint16_t time_window) {
+ auto m = NitrokeyManager::instance();
+ return m->write_TOTP_slot(slot_number, secret, time_window);
+}
+
+extern const char* NK_get_totp_slot_name(uint8_t slot_number){
+ auto m = NitrokeyManager::instance();
+ return m->get_totp_slot_name(slot_number);
+}
+extern const char* NK_get_hotp_slot_name(uint8_t slot_number){
+ auto m = NitrokeyManager::instance();
+ return m->get_hotp_slot_name(slot_number);
+}
+
+extern void NK_set_debug(bool state){
+ auto m = NitrokeyManager::instance();
+ m->set_debug(state);
+}
+
+
+} \ No newline at end of file
diff --git a/NK_C_API.h b/NK_C_API.h
new file mode 100644
index 0000000..28bc0fa
--- /dev/null
+++ b/NK_C_API.h
@@ -0,0 +1,25 @@
+#ifndef LIBNITROKEY_NK_C_API_H
+#define LIBNITROKEY_NK_C_API_H
+
+#include <iostream>
+#include <string>
+#include "include/NitrokeyManager.h"
+#include "include/inttypes.h"
+
+extern "C"
+{
+extern void NK_set_debug(bool state);
+extern int NK_login(const char *pin, const char *temporary_password);
+extern int NK_logout();
+extern const char * NK_status();
+extern const char * NK_get_totp_slot_name(uint8_t slot_number);
+extern const char * NK_get_hotp_slot_name(uint8_t slot_number);
+extern int NK_erase_slot(uint8_t slot_number);
+extern int NK_write_hotp_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint8_t hotp_counter, const char *temporary_password);
+extern int NK_write_totp_slot(uint8_t slot_number, const char *secret, uint16_t time_window);
+extern uint32_t NK_get_hotp_code(uint8_t slot_number);
+extern uint32_t NK_get_totp_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time, uint8_t last_interval);
+}
+
+
+#endif //LIBNITROKEY_NK_C_API_H
diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc
new file mode 100644
index 0000000..70b0132
--- /dev/null
+++ b/NitrokeyManager.cc
@@ -0,0 +1,151 @@
+#include <cassert>
+#include <cstring>
+#include "include/NitrokeyManager.h"
+
+namespace nitrokey{
+
+ template <typename T>
+ void initialize(T& st){ bzero(&st, sizeof(st)); }
+
+ NitrokeyManager * NitrokeyManager::_instance = nullptr;
+
+ NitrokeyManager::NitrokeyManager(): device(nullptr) {
+ set_debug(true);
+ }
+ NitrokeyManager::~NitrokeyManager() {delete _instance; delete device;}
+
+ bool NitrokeyManager::connect() {
+ device = new Stick10();
+ return device->connect();
+ }
+
+ NitrokeyManager *NitrokeyManager::instance() {
+ if (_instance == nullptr){
+ _instance = new NitrokeyManager();
+ }
+ return _instance;
+ }
+
+ bool NitrokeyManager::disconnect() {
+ return device->disconnect();
+ }
+
+ void NitrokeyManager::set_debug(bool state) {
+ if (state){
+ Log::instance().set_loglevel(Loglevel::DEBUG_L2);
+ } else {
+ Log::instance().set_loglevel(Loglevel::ERROR);
+ }
+ }
+
+ string NitrokeyManager::get_status() {
+ auto response = GetStatus::CommandTransaction::run(*device);
+ return response.dissect();
+ }
+
+ uint32_t NitrokeyManager::get_HOTP_code(uint8_t slot_number) {
+ assert(is_valid_hotp_slot_number(slot_number));
+ GetHOTP::CommandTransaction::CommandPayload gh;
+ gh.slot_number = get_internal_slot_number_for_hotp(slot_number);
+ auto resp = GetHOTP::CommandTransaction::run(*device, gh);
+ return resp.code;
+ }
+
+
+ bool NitrokeyManager::is_valid_hotp_slot_number(uint8_t slot_number) const { return slot_number < 3; }
+ bool NitrokeyManager::is_valid_totp_slot_number(uint8_t slot_number) const { return slot_number < 0x10; }
+ uint8_t NitrokeyManager::get_internal_slot_number_for_totp(uint8_t slot_number) const { return (uint8_t) (0x20 + slot_number); }
+ uint8_t NitrokeyManager::get_internal_slot_number_for_hotp(uint8_t slot_number) const { return (uint8_t) (0x10 + slot_number); }
+
+ uint32_t NitrokeyManager::get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
+ uint8_t last_interval) {
+ assert(is_valid_totp_slot_number(slot_number));
+ GetTOTP::CommandTransaction::CommandPayload gt;
+ gt.slot_number = slot_number;
+ gt.challenge = challenge;
+ gt.last_interval = last_interval;
+ gt.last_totp_time = last_totp_time;
+ auto resp = GetTOTP::CommandTransaction::run(*device, gt);
+ return resp.code;
+ }
+
+ bool NitrokeyManager::erase_slot(uint8_t slot_number) {
+ EraseSlot::CommandTransaction::CommandPayload p;
+ p.slot_number = slot_number;
+ auto resp = EraseSlot::CommandTransaction::run(*device,p);
+ return true;
+ }
+
+ bool NitrokeyManager::erase_hotp_slot(uint8_t slot_number) {
+ assert(is_valid_hotp_slot_number(slot_number));
+ slot_number = get_internal_slot_number_for_hotp(slot_number);
+ return erase_slot(slot_number);
+ }
+
+ bool NitrokeyManager::erase_totp_slot(uint8_t slot_number) {
+ assert(is_valid_totp_slot_number(slot_number));
+ slot_number = get_internal_slot_number_for_totp(slot_number);
+ return erase_slot(slot_number);
+ }
+
+ bool NitrokeyManager::write_HOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
+ const char *temporary_password) {
+ assert(is_valid_hotp_slot_number(slot_number));
+ assert(strlen(secret)==20); //160 bits
+ assert(strlen(slot_name)<=15);
+
+ slot_number = get_internal_slot_number_for_hotp(slot_number);
+ WriteToHOTPSlot::CommandPayload payload;
+ payload.slot_number = slot_number;
+ strcpy((char *) payload.slot_secret, secret);
+ strcpy((char *) payload.slot_name, slot_name);
+ payload.slot_counter = hotp_counter;
+ payload.slot_config;
+ memset(payload.slot_token_id, 0, sizeof(payload.slot_token_id)); //?????
+
+ Authorize::CommandPayload auth;
+ auth.initialize();
+ strcpy((char *) (auth.temporary_password), temporary_password);
+ auth.crc_to_authorize = auth.crc_to_authorize = WriteToHOTPSlot::CommandTransaction::getCRC(payload);
+ Authorize::CommandTransaction::run(*device, auth);
+
+ auto resp = WriteToHOTPSlot::CommandTransaction::run(*device, payload);
+ return false;
+ }
+
+ bool NitrokeyManager::write_TOTP_slot(uint8_t slot_number, const char *secret, uint16_t time_window) {
+ assert(is_valid_totp_slot_number(slot_number));
+ slot_number = get_internal_slot_number_for_totp(slot_number);
+ return false;
+ }
+
+ const char * NitrokeyManager::get_totp_slot_name(uint8_t slot_number) {
+ assert(is_valid_totp_slot_number(slot_number));
+ slot_number = get_internal_slot_number_for_totp(slot_number);
+ return (const char *) get_slot_name(slot_number);
+ }
+ const char * NitrokeyManager::get_hotp_slot_name(uint8_t slot_number) {
+ assert(is_valid_hotp_slot_number(slot_number));
+ slot_number = get_internal_slot_number_for_hotp(slot_number);
+ return (const char *) get_slot_name(slot_number);
+ }
+
+ uint8_t *NitrokeyManager::get_slot_name(uint8_t slot_number) const {
+ GetSlotName::CommandPayload payload;
+ payload.slot_number = slot_number;
+ auto resp = GetSlotName::CommandTransaction::run(*device, payload);
+ return (uint8_t *) strdup((const char *) resp.slot_name);
+ }
+
+ bool NitrokeyManager::authorize(const char *pin, const char *temporary_password) {
+ FirstAuthenticate::CommandPayload authreq;
+ initialize(authreq); //TODO
+ authreq.initialize();
+ strcpy((char *) (authreq.card_password), pin);
+ strcpy((char *) (authreq.temporary_password), temporary_password);
+ FirstAuthenticate::CommandTransaction::run(*device, authreq);
+ return true;
+ }
+
+
+} \ No newline at end of file
diff --git a/build/test.py b/build/test.py
new file mode 100644
index 0000000..fa3a863
--- /dev/null
+++ b/build/test.py
@@ -0,0 +1,57 @@
+import cffi
+
+ffi = cffi.FFI()
+fp = '../NK_C_API.h'
+
+declarations = []
+with open(fp, 'r') as f:
+ declarations = f.readlines()
+
+for declaration in declarations:
+ # extern int NK_write_totp_slot(int slot_number, char* secret, int time_window);
+ if 'extern' in declaration and not '"C"' in declaration:
+ declaration = declaration.replace('extern', '').strip()
+ print(declaration)
+ ffi.cdef(declaration)
+
+C = ffi.dlopen("./libnitrokey.so")
+
+if __name__ == "__main__":
+ C.NK_set_debug(False)
+ C.NK_set_debug(True)
+ a = C.NK_login('12345678', '123123123')
+ # a = C.NK_logout()
+ print(a)
+ C.NK_set_debug(False)
+
+ # print(''.center(40, '#'))
+ print(ffi.string(C.NK_status()))
+ # print(''.center(40, '#'))
+
+ # print(C.NK_get_hotp_code(0))
+ # print(C.NK_get_totp_code(0, 0, 0, 0))
+ # print(ffi.string(C.NK_get_totp_slot_name(0)))
+
+ s = []
+ for i in range(16):
+ s.append(ffi.string(C.NK_get_totp_slot_name(i)))
+ for i in range(3):
+ s.append(ffi.string(C.NK_get_hotp_slot_name(i)))
+ print(repr(s))
+ print((s))
+
+ s = []
+ for i in range(16):
+ s.append(C.NK_get_totp_code(i, 0, 0, 0))
+ for i in range(3):
+ s.append(C.NK_get_hotp_code(i))
+ print(repr(s))
+ print((s))
+ C.NK_set_debug(True)
+
+ s = []
+ C.NK_write_hotp_slot(1, 'python_test', '12345678901234567890', 0, '123123123')
+ C.NK_set_debug(False)
+ for i in range(3):
+ s.append(C.NK_get_hotp_code(1))
+ print((s))
diff --git a/device.cc b/device.cc
index 08cba49..3075c0d 100644
--- a/device.cc
+++ b/device.cc
@@ -3,9 +3,9 @@
#include <cstddef>
#include <stdexcept>
#include <hidapi/hidapi.h>
-#include "device.h"
-#include "log.h"
-#include "misc.h"
+#include "include/misc.h"
+#include "include/device.h"
+#include "include/log.h"
using namespace nitrokey::device;
using namespace nitrokey::log;
diff --git a/include/NitrokeyManager.h b/include/NitrokeyManager.h
new file mode 100644
index 0000000..37b628d
--- /dev/null
+++ b/include/NitrokeyManager.h
@@ -0,0 +1,56 @@
+#ifndef LIBNITROKEY_NITROKEYMANAGER_H
+#define LIBNITROKEY_NITROKEYMANAGER_H
+
+#include "device.h"
+#include "log.h"
+#include "device_proto.h"
+#include "stick10_commands.h"
+
+namespace nitrokey {
+ using namespace nitrokey::device;
+ using namespace std;
+ using namespace nitrokey::proto::stick10;
+ using namespace nitrokey::proto;
+ using namespace nitrokey::log;
+
+ class NitrokeyManager {
+ public:
+ static NitrokeyManager *instance();
+
+ bool authorize(const char *pin, const char *temporary_password);
+ bool write_HOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint64_t hotp_counter,
+ const char *temporary_password);
+ bool write_TOTP_slot(uint8_t slot_number, const char *secret, uint16_t time_window);
+ uint32_t get_HOTP_code(uint8_t slot_number);
+ uint32_t get_TOTP_code(uint8_t slot_number, uint64_t challenge, uint64_t last_totp_time,
+ uint8_t last_interval);
+ bool erase_totp_slot(uint8_t slot_number);
+ bool erase_hotp_slot(uint8_t slot_number);
+ bool connect();
+ bool disconnect();
+ void set_debug(bool state);
+ string get_status();
+
+ const char * get_totp_slot_name(uint8_t slot_number);
+ const char * get_hotp_slot_name(uint8_t slot_number);
+
+ private:
+ NitrokeyManager();
+ ~NitrokeyManager();
+
+ static NitrokeyManager *_instance;
+ bool connected;
+ Device *device;
+
+ bool is_valid_hotp_slot_number(uint8_t slot_number) const;
+ bool is_valid_totp_slot_number(uint8_t slot_number) const;
+ uint8_t get_internal_slot_number_for_hotp(uint8_t slot_number) const;
+ uint8_t get_internal_slot_number_for_totp(uint8_t slot_number) const;
+ bool erase_slot(uint8_t slot_number);
+ uint8_t *get_slot_name(uint8_t slot_number) const;
+ };
+}
+
+
+
+#endif //LIBNITROKEY_NITROKEYMANAGER_H
diff --git a/include/dissect.h b/include/dissect.h
index 993d348..ab94e62 100644
--- a/include/dissect.h
+++ b/include/dissect.h
@@ -5,6 +5,7 @@
#define DISSECT_H
#include <string>
#include <sstream>
+#include <iomanip>
#include "misc.h"
#include "cxx_semantics.h"
#include "command_id.h"
@@ -66,7 +67,8 @@ class ResponseDissector : semantics::non_constructible {
<< 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 CRC:\t" << std::hex << std::setw(2) << std::setfill('0')
+ << 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;
diff --git a/include/stick10_commands.h b/include/stick10_commands.h
index f1903aa..b7f6de0 100644
--- a/include/stick10_commands.h
+++ b/include/stick10_commands.h
@@ -1,9 +1,12 @@
#ifndef STICK10_COMMANDS_H
#define STICK10_COMMANDS_H
+#include <bitset>
+#include <iomanip>
#include <string>
#include <sstream>
#include "inttypes.h"
#include "command.h"
+#include "device_proto.h"
namespace nitrokey {
namespace proto {
@@ -18,13 +21,23 @@ class GetSlotName : public Command<CommandID::READ_SLOT_NAME> {
struct CommandPayload {
uint8_t slot_number;
- bool isValid() const { return !(slot_number & 0xF0); }
+ bool isValid() const { return slot_number<0x10+3; }
+ 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];
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,
@@ -37,6 +50,11 @@ class EraseSlot : Command<CommandID::ERASE_SLOT> {
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;
typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
@@ -56,6 +74,7 @@ class SetTime : Command<CommandID::SET_TIME> {
CommandTransaction;
};
+
// TODO duplicate TOTP
class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
public:
@@ -65,7 +84,7 @@ class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
uint8_t slot_secret[20];
uint8_t slot_config;
uint8_t slot_token_id[13];
- uint8_t slot_counter[8];
+ uint64_t slot_counter;
bool isValid() const { return !(slot_number & 0xF0); }
std::string dissect() const {
@@ -73,9 +92,12 @@ class WriteToHOTPSlot : Command<CommandID::WRITE_TO_SLOT> {
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;
+ ss << "slot_config:\t" << std::bitset<8>((int)slot_config) << std::endl;
+ ss << "slot_token_id:\t";
+ for (auto i : slot_token_id)
+ ss << std::hex << std::setw(2) << std::setfill('0')<< (int) i << " " ;
+ ss << std::endl;
+ ss << "slot_counter:\t" << (int)slot_counter << std::endl;
return ss.str();
}
} __packed;
@@ -122,6 +144,47 @@ class GetCode : Command<CommandID::GET_CODE> {
struct ResponsePayload> CommandTransaction;
};
+class GetTOTP : 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); }
+ std::string dissect() const {
+ std::stringstream ss;
+ ss << "slot_number:\t" << (int)(slot_number) << std::endl;
+ ss << "challenge:\t" << (challenge) << std::endl;
+ ss << "last_totp_time:\t" << (last_totp_time) << std::endl;
+ ss << "last_interval:\t" << (int)(last_interval) << 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 GetHOTP : Command<CommandID::GET_CODE> {
public:
struct CommandPayload {
@@ -444,6 +507,15 @@ class WriteGeneralConfig : Command<CommandID::WRITE_CONFIG> {
CommandTransaction;
};
+// struct clear_on_const {
+// clear_on_const(){
+// initialize();
+// }
+// void initialize(){
+// bzero(this, sizeof(*this));
+// }
+// };
+
class FirstAuthenticate : Command<CommandID::FIRST_AUTHENTICATE> {
public:
struct CommandPayload {
@@ -451,6 +523,7 @@ class FirstAuthenticate : Command<CommandID::FIRST_AUTHENTICATE> {
uint8_t temporary_password[25];
bool isValid() const { return true; }
+ void initialize(){ bzero(this, sizeof(*this)); }
std::string dissect() const {
std::stringstream ss;
@@ -480,12 +553,14 @@ class UserAuthenticate : Command<CommandID::USER_AUTHENTICATE> {
class Authorize : Command<CommandID::AUTHORIZE> {
public:
struct CommandPayload {
- uint32_t crc_to_authorize;
+ uint32_t crc_to_authorize;
uint8_t temporary_password[25];
- std::string dissect() const {
+ void initialize(){ bzero(this, sizeof(*this)); }
+
+ std::string dissect() const {
std::stringstream ss;
- ss << " crc_to_authorize:\t" << crc_to_authorize<< std::endl;
+ ss << " crc_to_authorize:\t" << std::hex << std::setw(2) << std::setfill('0') << crc_to_authorize<< std::endl;
ss << " temporary_password:\t" << temporary_password<< std::endl;
return ss.str();
}
diff --git a/python_bindings/api.cpp b/python_bindings/api.cpp
new file mode 100644
index 0000000..57344c9
--- /dev/null
+++ b/python_bindings/api.cpp
@@ -0,0 +1,77 @@
+
+#include <iostream>
+//#include "toplevel.h"
+#include "../include/device.h"
+#include "../include/stick10_commands.h"
+#include "../include/log.h"
+#include "../include/device_proto.h"
+#include <cstdlib>
+#include <cstring>
+
+using namespace std;
+using namespace nitrokey::device;
+using namespace nitrokey::proto::stick10;
+using namespace nitrokey::proto;
+using namespace nitrokey::log;
+
+/*
+- manage (=create, change, delete, list, read) OTP entries
+- use/generate OTPs
+- Change PINs (so that the user doesn't need any other tool other than
+his Python application)
+* */
+
+
+bool writeHOTPSlot(Device &stick, int slotNumber, const char *slotName, const char *temporary_password,
+ const char *secret);
+bool authenticate(Device &stick, const char *card_password, const char *temporary_password);
+
+int NK_login(char *user_type, char *pin);
+int NK_logout();
+
+//some_struct
+void NK_list_slots();
+int NK_erase_slot(int slot_num);
+int NK_erase_totp_slot(int slot_num);
+int NK_erase_hotp_slot(int slot_num);
+int NK_write_hotp_slot(char *secret, int hotp_counter);
+int NK_write_totp_slot(char *secret, int time_window);
+int NK_change_PIN();
+
+void initHotp(const char *card_password, int slot, const char *slot_name, const char *secret) {
+ //Log::instance().set_loglevel(Loglevel::DEBUG);
+ Stick10 stick;
+ bool connected = stick.connect();
+ auto response = GetStatus::CommandTransaction::run(stick);
+ const char *temporary_password = "123456789012345678901234";
+ bool success = authenticate(stick, card_password, temporary_password);
+// hexStringToByte(hwrite.slot_secret, hexSecret);
+ success = writeHOTPSlot(stick, slot, slot_name, temporary_password, secret);
+ stick.disconnect();
+}
+
+bool writeHOTPSlot(Device &stick, int slotNumber, const char *slotName, const char *temporary_password,
+ const char *secret) {
+ Transaction::CommandPayload hwrite;
+ hwrite.slot_number = slotNumber;
+ strcpy(reinterpret_cast<char *>(hwrite.slot_name), slotName);
+ strcpy(reinterpret_cast<char *>(hwrite.slot_secret), secret);
+
+ //authorize writehotp first
+ Transaction::CommandPayload auth;
+ strcpy((char *) (auth.temporary_password), temporary_password);
+ auth.crc_to_authorize = auth.crc_to_authorize = WriteToHOTPSlot::CommandTransaction::getCRC(hwrite);
+ Authorize::CommandTransaction::run(stick, auth);
+
+ //run hotp command
+ WriteToHOTPSlot::CommandTransaction::run(stick, hwrite);
+ return true;
+}
+
+bool authenticate(Device &stick, const char *card_password, const char *temporary_password) {
+ Transaction::CommandPayload authreq;
+ strcpy((char *) (authreq.card_password), card_password);
+ strcpy((char *) (authreq.temporary_password), temporary_password);
+ FirstAuthenticate::CommandTransaction::run(stick, authreq);
+ return true;
+}
diff --git a/python_bindings/pybind11 b/python_bindings/pybind11
new file mode 160000
+Subproject 1f66a584278dfd1ad88be19d5e4996302793a19
diff --git a/unittest/build/run.sh b/unittest/build/run.sh
new file mode 100755
index 0000000..2bcc580
--- /dev/null
+++ b/unittest/build/run.sh
@@ -0,0 +1 @@
+LD_LIBRARY_PATH=. ./test_HOTP
diff --git a/unittest/test_HOTP.cc b/unittest/test_HOTP.cc
index 1f54376..a961b24 100644
--- a/unittest/test_HOTP.cc
+++ b/unittest/test_HOTP.cc
@@ -1,8 +1,6 @@
#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"
@@ -21,14 +19,14 @@ void hexStringToByte(uint8_t data[], const char* hexString){
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);
+ hexStringToByte(slot_secret, secretHex);
CAPTURE(slot_secret);
REQUIRE(strcmp("12345678901234567890",reinterpret_cast<char *>(slot_secret) ) == 0 );
}
@@ -60,7 +58,7 @@ TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") {
const char* secretHex = "3132333435363738393031323334353637383930";
hexStringToByte(hwrite.slot_secret, secretHex);
// reset the HOTP counter
- memset(hwrite.slot_counter, 0, 8);
+// memset(hwrite.slot_counter, 0, 8);
//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), "");
@@ -72,12 +70,12 @@ TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") {
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,
+ 755224, 287082, 359152, 969429, 338314,
254676, 287922, 162583, 399871, 520489
};