aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorszszszsz <szszszsz@users.noreply.github.com>2016-09-10 11:02:38 +0200
committerGitHub <noreply@github.com>2016-09-10 11:02:38 +0200
commitb16e89ad4445fe9bbb66e8e7f8771a6ca6b333cf (patch)
treed332db36123c80ac84474c75b9be4acdff81bf54
parente164c5f3dc74fb2335b1fc573ce446cdd76a07dc (diff)
parenta46491a97da08e495c92bba8046426678b5564f7 (diff)
downloadlibnitrokey-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.cc16
-rw-r--r--include/LibraryException.h41
-rw-r--r--include/misc.h4
-rw-r--r--misc.cc28
-rw-r--r--unittest/test.cc12
-rw-r--r--unittest/test_HOTP.cc2
-rw-r--r--unittest/test_bindings.py33
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);
}
}
diff --git a/misc.cc b/misc.cc
index d004d0f..9b339cd 100644
--- a/misc.cc
+++ b/misc.cc
@@ -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