Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 31 secret as hex #36

Merged
merged 4 commits into from
Sep 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions NitrokeyManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <iostream>
#include "include/NitrokeyManager.h"
#include "include/LibraryException.h"
#include <algorithm>

namespace nitrokey{

Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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
Expand Down
41 changes: 41 additions & 0 deletions include/LibraryException.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,47 @@ class LibraryException: std::exception {



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 {
Expand Down
4 changes: 3 additions & 1 deletion include/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define MISC_H
#include <stdio.h>
#include <string>
#include <vector>

namespace nitrokey {
namespace misc {
Expand All @@ -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);
}
}

Expand Down
28 changes: 28 additions & 0 deletions misc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
12 changes: 6 additions & 6 deletions unittest/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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"));
}

Expand All @@ -67,15 +67,15 @@ 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"));
}

{
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"));
}

Expand Down
2 changes: 1 addition & 1 deletion unittest/test_HOTP.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
/*
Expand Down
33 changes: 31 additions & 2 deletions unittest/test_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@
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'
Expand All @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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