diff options
| author | szszszsz <szszszsz@users.noreply.github.com> | 2016-09-10 11:02:38 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-09-10 11:02:38 +0200 | 
| commit | b16e89ad4445fe9bbb66e8e7f8771a6ca6b333cf (patch) | |
| tree | d332db36123c80ac84474c75b9be4acdff81bf54 | |
| parent | e164c5f3dc74fb2335b1fc573ce446cdd76a07dc (diff) | |
| parent | a46491a97da08e495c92bba8046426678b5564f7 (diff) | |
| download | libnitrokey-b16e89ad4445fe9bbb66e8e7f8771a6ca6b333cf.tar.gz libnitrokey-b16e89ad4445fe9bbb66e8e7f8771a6ca6b333cf.tar.bz2 | |
Merge pull request #36 from Nitrokey/issue_31-secret_as_hex
#31 pass secret to OTP as hex (breaking change - previously any string was accepted)
| -rw-r--r-- | NitrokeyManager.cc | 16 | ||||
| -rw-r--r-- | include/LibraryException.h | 41 | ||||
| -rw-r--r-- | include/misc.h | 4 | ||||
| -rw-r--r-- | misc.cc | 28 | ||||
| -rw-r--r-- | unittest/test.cc | 12 | ||||
| -rw-r--r-- | unittest/test_HOTP.cc | 2 | ||||
| -rw-r--r-- | unittest/test_bindings.py | 33 | 
7 files changed, 124 insertions, 12 deletions
| diff --git a/NitrokeyManager.cc b/NitrokeyManager.cc index 5b648b5..d827292 100644 --- a/NitrokeyManager.cc +++ b/NitrokeyManager.cc @@ -2,6 +2,7 @@  #include <iostream>  #include "include/NitrokeyManager.h"  #include "include/LibraryException.h" +#include <algorithm>  namespace nitrokey{ @@ -157,6 +158,15 @@ namespace nitrokey{          return erase_slot(slot_number, temporary_password);      } +    template <typename T, typename U> +    void vector_copy(T& dest, std::vector<U> &vec){ +        const size_t d_size = sizeof(dest); +        if(d_size < vec.size()){ +            throw TargetBufferSmallerThanSource(vec.size(), d_size); +        } +        std::fill(dest, dest+d_size, 0); +        std::copy(vec.begin(), vec.end(), dest); +    }      bool NitrokeyManager::write_HOTP_slot(uint8_t slot_number, const char *slot_name, const char *secret, uint8_t hotp_counter,                                                bool use_8_digits, bool use_enter, bool use_tokenID, const char *token_ID, @@ -166,7 +176,8 @@ namespace nitrokey{          slot_number = get_internal_slot_number_for_hotp(slot_number);          auto payload = get_payload<WriteToHOTPSlot>();          payload.slot_number = slot_number; -        strcpyT(payload.slot_secret, secret); +        auto secret_bin = misc::hex_string_to_byte(secret); +        vector_copy(payload.slot_secret, secret_bin);          strcpyT(payload.slot_name, slot_name);          strcpyT(payload.slot_token_id, token_ID);          payload.slot_counter = hotp_counter; @@ -188,7 +199,8 @@ namespace nitrokey{          slot_number = get_internal_slot_number_for_totp(slot_number);          payload.slot_number = slot_number; -        strcpyT(payload.slot_secret, secret); +        auto secret_bin = misc::hex_string_to_byte(secret); +        vector_copy(payload.slot_secret, secret_bin);          strcpyT(payload.slot_name, slot_name);          strcpyT(payload.slot_token_id, token_ID);          payload.slot_interval = time_window; //FIXME naming diff --git a/include/LibraryException.h b/include/LibraryException.h index 5036320..72891fb 100644 --- a/include/LibraryException.h +++ b/include/LibraryException.h @@ -12,6 +12,47 @@ public: +class TargetBufferSmallerThanSource: public LibraryException { +public: +    virtual uint8_t exception_id() override { +        return 203; +    } + +public: +    size_t source_size; +    size_t target_size; + +    TargetBufferSmallerThanSource( +            size_t source_size, size_t target_size +            ) : source_size(source_size),  target_size(target_size) {} + +    virtual const char *what() const throw() override { +        std::string s = " "; +        auto ts = [](int x){ return std::to_string(x); }; +        std::string msg = std::string("Target buffer size is smaller than source: [source size, buffer size]") +            +s+ ts(source_size) +s+ ts(target_size); +        return msg.c_str(); +    } + +}; + +class InvalidHexString : public LibraryException { +public: +    virtual uint8_t exception_id() override { +        return 202; +    } + +public: +    uint8_t invalid_char; + +    InvalidHexString (uint8_t invalid_char) : invalid_char( invalid_char) {} + +    virtual const char *what() const throw() override { +        return "Invalid character in hex string"; +    } + +}; +  class InvalidSlotException : public LibraryException {  public:      virtual uint8_t exception_id() override { diff --git a/include/misc.h b/include/misc.h index cccd830..5fcd16d 100644 --- a/include/misc.h +++ b/include/misc.h @@ -2,6 +2,7 @@  #define MISC_H  #include <stdio.h>  #include <string> +#include <vector>  namespace nitrokey {  namespace misc { @@ -16,7 +17,8 @@ typename T::CommandPayload get_payload(){      std::string hexdump(const char *p, size_t size, bool print_header=true); -uint32_t stm_crc32(const uint8_t *data, size_t size); +    uint32_t stm_crc32(const uint8_t *data, size_t size); +    std::vector<uint8_t> hex_string_to_byte(const char* hexString);  }  } @@ -2,10 +2,38 @@  #include <string>  #include "misc.h"  #include "inttypes.h" +#include <cstdlib> +#include <cstring> +#include "LibraryException.h"  namespace nitrokey {  namespace misc { +std::vector<uint8_t> hex_string_to_byte(const char* hexString){ +    const size_t big_string_size = 256; //arbitrary 'big' number +    const size_t s_size = strlen(hexString); +    const size_t d_size = s_size/2; +    if (s_size%2!=0 || s_size==0 || s_size>big_string_size){ +        throw InvalidHexString(0); +    } +    auto data = std::vector<uint8_t>(d_size, 0); + +    char buf[2]; +    for(int i=0; i<s_size; i++){ + +        char c = hexString[i]; +        bool char_from_range = (('0' <= c && c <='9') || ('A' <= c && c <= 'F') || ('a' <= c && c<= 'f')); +        if (!char_from_range){ +            throw InvalidHexString(c); +        } +        buf[i%2] = c; +        if (i%2==1){ +            data[i/2] = strtoul(buf, NULL, 16) & 0xFF; +        } +    } +    return data; +}; +  std::string hexdump(const char *p, size_t size, bool print_header) {    std::stringstream out;    char formatbuf[128]; diff --git a/unittest/test.cc b/unittest/test.cc index 6267512..6744b45 100644 --- a/unittest/test.cc +++ b/unittest/test.cc @@ -18,7 +18,7 @@ std::string getSlotName(Stick10 &stick, int slotNo) {    auto slot_req = get_payload<ReadSlot>();    slot_req.slot_number = slotNo;    auto slot = ReadSlot::CommandTransaction::run(stick, slot_req); -  std::string sName(reinterpret_cast<char *>(slot.slot_name)); +  std::string sName(reinterpret_cast<char *>(slot.data().slot_name));    return sName;  } @@ -47,18 +47,18 @@ TEST_CASE("Slot names are correct", "[slotNames]") {    {      auto resp = GetPasswordRetryCount::CommandTransaction::run(stick); -    REQUIRE(resp.password_retry_count == 3); +    REQUIRE(resp.data().password_retry_count == 3);    }    {      auto resp = GetUserPasswordRetryCount::CommandTransaction::run(stick); -    REQUIRE(resp.password_retry_count == 3); +    REQUIRE(resp.data().password_retry_count == 3);    }    {      auto slot = get_payload<GetPasswordSafeSlotName>();      slot.slot_number = 0;      auto resp2 = GetPasswordSafeSlotName::CommandTransaction::run(stick, slot); -    std::string sName(reinterpret_cast<char *>(resp2.slot_name)); +    std::string sName(reinterpret_cast<char *>(resp2.data().slot_name));      REQUIRE(sName == std::string("web1"));    } @@ -67,7 +67,7 @@ TEST_CASE("Slot names are correct", "[slotNames]") {      slot.slot_number = 0;      auto resp2 =          GetPasswordSafeSlotPassword::CommandTransaction::run(stick, slot); -    std::string sName(reinterpret_cast<char *>(resp2.slot_password)); +    std::string sName(reinterpret_cast<char *>(resp2.data().slot_password));      REQUIRE(sName == std::string("pass1"));    } @@ -75,7 +75,7 @@ TEST_CASE("Slot names are correct", "[slotNames]") {      auto slot = get_payload<GetPasswordSafeSlotLogin>();      slot.slot_number = 0;      auto resp2 = GetPasswordSafeSlotLogin::CommandTransaction::run(stick, slot); -    std::string sName(reinterpret_cast<char *>(resp2.slot_login)); +    std::string sName(reinterpret_cast<char *>(resp2.data().slot_login));      REQUIRE(sName == std::string("login1"));    } diff --git a/unittest/test_HOTP.cc b/unittest/test_HOTP.cc index f25bad4..d31df55 100644 --- a/unittest/test_HOTP.cc +++ b/unittest/test_HOTP.cc @@ -84,7 +84,7 @@ TEST_CASE("Test HOTP codes according to RFC", "[HOTP]") {          auto gh = get_payload<GetHOTP>();          gh.slot_number =  0x10;          auto resp = GetHOTP::CommandTransaction::run(stick, gh); -        REQUIRE( resp.code == code); +        REQUIRE( resp.data().code == code);      }      //checking slot programmed before with nitro-app      /* diff --git a/unittest/test_bindings.py b/unittest/test_bindings.py index eeda247..9c266aa 100644 --- a/unittest/test_bindings.py +++ b/unittest/test_bindings.py @@ -5,8 +5,16 @@ from enum import Enum  ffi = cffi.FFI()  gs = ffi.string -RFC_SECRET = '12345678901234567890' +def to_hex(s): +    return "".join("{:02x}".format(ord(c)) for c in s) + + +RFC_SECRET_HR = '12345678901234567890' +RFC_SECRET = to_hex(RFC_SECRET_HR)  # '12345678901234567890' + + +# print( repr((RFC_SECRET, RFC_SECRET_, len(RFC_SECRET))) )  class DefaultPasswords(Enum):      ADMIN = '12345678' @@ -26,6 +34,8 @@ class DeviceErrorCode(Enum):  class LibraryErrors(Enum):      TOO_LONG_STRING = 200      INVALID_SLOT = 201 +    INVALID_HEX_STRING = 202 +    TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE = 203  @pytest.fixture(scope="module") @@ -214,7 +224,7 @@ def test_invalid_slot(C):      invalid_slot = 255      assert C.NK_erase_totp_slot(invalid_slot, 'some password') == LibraryErrors.INVALID_SLOT      assert C.NK_write_hotp_slot(invalid_slot, 'long_test', RFC_SECRET, 0, False, False, False, "", -                            'aaa') == LibraryErrors.INVALID_SLOT +                                'aaa') == LibraryErrors.INVALID_SLOT      assert C.NK_get_hotp_code_PIN(invalid_slot, 'some password') == 0      assert C.NK_get_last_command_status() == LibraryErrors.INVALID_SLOT      assert C.NK_erase_password_safe_slot(invalid_slot) == LibraryErrors.INVALID_SLOT @@ -503,3 +513,22 @@ def test_get_serial_number(C):      sn = gs(sn)      assert len(sn) > 0      print(('Serial number of the device: ', sn)) + + +@pytest.mark.parametrize("invalid_hex_string", +                         ['text', '00  ', '0xff', 'zzzzzzzzzzzz', 'fff', '', 'f' * 257, 'f' * 258]) +def test_invalid_secret_hex_string_for_OTP_write(C, invalid_hex_string): +    """ +    Tests for invalid secret hex string during writing to OTP slot. Invalid strings are not hexadecimal number, +    empty or longer than 255 characters. +    """ +    assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '', +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING +    assert C.NK_write_totp_slot(1, 'python_test', invalid_hex_string, 30, True, False, False, "", +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.INVALID_HEX_STRING + + +def test_warning_binary_bigger_than_secret_buffer(C): +    invalid_hex_string = to_hex('1234567890') * 3 +    assert C.NK_write_hotp_slot(1, 'slot_name', invalid_hex_string, 0, True, False, False, '', +                                DefaultPasswords.ADMIN_TEMP) == LibraryErrors.TARGET_BUFFER_SIZE_SMALLER_THAN_SOURCE | 
