diff options
| -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      }; | 
