diff options
author | Szczepan Zalega <szczepan@nitrokey.com> | 2016-07-18 18:04:34 +0200 |
---|---|---|
committer | Szczepan Zalega <szczepan@nitrokey.com> | 2016-08-01 13:54:45 +0200 |
commit | a5d11eab6003a6ed0f7c78ecb7136b28ee938a23 (patch) | |
tree | fa1c5a1a476f51161b822188508704c5acd52a1d | |
parent | 209672f9bba667ea0fc7bce364ef0ad1c8bc4d2a (diff) | |
download | libnitrokey-a5d11eab6003a6ed0f7c78ecb7136b28ee938a23.tar.gz libnitrokey-a5d11eab6003a6ed0f7c78ecb7136b28ee938a23.tar.bz2 |
Initial version of C/Python bindings
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | .idea/codeStyleSettings.xml | 38 | ||||
-rw-r--r-- | .idea/dictionaries/sz.xml | 8 | ||||
-rw-r--r-- | .idea/vcs.xml | 6 | ||||
-rw-r--r-- | CMakeLists.txt | 27 | ||||
-rw-r--r-- | NK_C_API.cc | 69 | ||||
-rw-r--r-- | NK_C_API.h | 25 | ||||
-rw-r--r-- | NitrokeyManager.cc | 151 | ||||
-rw-r--r-- | build/test.py | 57 | ||||
-rw-r--r-- | device.cc | 6 | ||||
-rw-r--r-- | include/NitrokeyManager.h | 56 | ||||
-rw-r--r-- | include/dissect.h | 4 | ||||
-rw-r--r-- | include/stick10_commands.h | 91 | ||||
-rw-r--r-- | python_bindings/api.cpp | 77 | ||||
m--------- | python_bindings/pybind11 | 0 | ||||
-rwxr-xr-x | unittest/build/run.sh | 1 | ||||
-rw-r--r-- | unittest/test_HOTP.cc | 14 |
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)) @@ -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 }; |