From 119ea0b111c9ca46fda32911c1c8c33b36aad3db Mon Sep 17 00:00:00 2001
From: Szczepan Zalega <szczepan@nitrokey.com>
Date: Tue, 8 Nov 2016 18:58:17 +0100
Subject: Authorization fix: GetHOTP and WriteGeneralConfig + test

Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
---
 command_id.cc                  |  3 ++
 include/stick10_commands_0.8.h | 83 +++++++++++++++++++++++++++++++++++++++++-
 unittest/test3.cc              | 69 ++++++++++++++++++++++++++++++++---
 3 files changed, 149 insertions(+), 6 deletions(-)

diff --git a/command_id.cc b/command_id.cc
index 9512b7d..a93d05c 100644
--- a/command_id.cc
+++ b/command_id.cc
@@ -139,6 +139,9 @@ const char *commandid_to_string(CommandID id) {
       return "DETECT_SC_AES";
     case CommandID::NEW_AES_KEY:
       return "NEW_AES_KEY";
+    case CommandID::WRITE_TO_SLOT_2:
+      return "WRITE_TO_SLOT_2";
+      break;
   }
   return "UNKNOWN";
 }
diff --git a/include/stick10_commands_0.8.h b/include/stick10_commands_0.8.h
index 037a777..0c6633c 100644
--- a/include/stick10_commands_0.8.h
+++ b/include/stick10_commands_0.8.h
@@ -12,6 +12,7 @@
 #include "inttypes.h"
 #include "command.h"
 #include "device_proto.h"
+#include "stick10_commands.h"
 
 namespace nitrokey {
     namespace proto {
@@ -20,6 +21,8 @@ namespace nitrokey {
  *	Stick10 protocol definition
  */
         namespace stick10_08 {
+            using stick10::FirstAuthenticate;
+            using stick10::UserAuthenticate;
 
             class EraseSlot : Command<CommandID::ERASE_SLOT> {
             public:
@@ -117,20 +120,68 @@ namespace nitrokey {
                     CommandTransaction;
             };
 
+            class GetHOTP : Command<CommandID::GET_CODE> {
+            public:
+                struct CommandPayload {
+                    uint8_t temporary_user_password[25];
+                    uint8_t slot_number;
+
+                    bool isValid() const { return (slot_number & 0xF0); }
+                    std::string dissect() const {
+                      std::stringstream ss;
+                      ss << "temporary_user_password:\t" << temporary_user_password << std::endl;
+                      ss << "slot_number:\t" << (int)(slot_number) << std::endl;
+                      return ss.str();
+                    }
+                } __packed;
+
+                struct ResponsePayload {
+                    union {
+                        uint8_t whole_response[18]; //14 bytes reserved for config, but used only 1
+                        struct {
+                            uint32_t code;
+                            union{
+                                uint8_t _slot_config;
+                                struct{
+                                    bool use_8_digits   : 1;
+                                    bool use_enter      : 1;
+                                    bool use_tokenID    : 1;
+                                };
+                            };
+                        } __packed;
+                    } __packed;
+
+                    bool isValid() const { return true; }
+                    std::string dissect() const {
+                      std::stringstream ss;
+                      ss << "code:\t" << (code) << std::endl;
+                      ss << "slot_config:\t" << std::bitset<8>((int)_slot_config) << std::endl;
+                      ss << "\tuse_8_digits(0):\t" << use_8_digits << std::endl;
+                      ss << "\tuse_enter(1):\t" << use_enter << std::endl;
+                      ss << "\tuse_tokenID(2):\t" << use_tokenID << std::endl;
+                      return ss.str();
+                    }
+                } __packed;
+
+                typedef Transaction<command_id(), struct CommandPayload, struct ResponsePayload>
+                    CommandTransaction;
+            };
+
 
             class GetTOTP : Command<CommandID::GET_CODE> {
                 //user auth
             public:
                 struct CommandPayload {
+                    uint8_t temporary_user_password[25];
                     uint8_t slot_number;
                     uint64_t challenge;
                     uint64_t last_totp_time;
                     uint8_t last_interval;
-                    uint8_t user_temporary_password[25];
 
                     bool isValid() const { return !(slot_number & 0xF0); }
                     std::string dissect() const {
                       std::stringstream ss;
+                      ss << "temporary_user_password:\t" << temporary_user_password << std::endl;
                       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;
@@ -172,6 +223,36 @@ namespace nitrokey {
             };
 
 
+            class WriteGeneralConfig : Command<CommandID::WRITE_CONFIG> {
+                //admin auth
+            public:
+                struct CommandPayload {
+                    union{
+                        uint8_t config[5];
+                        struct{
+                            uint8_t numlock;     /** 0-1: HOTP slot number from which the code will be get on double press, other value - function disabled */
+                            uint8_t capslock;    /** same as numlock */
+                            uint8_t scrolllock;  /** same as numlock */
+                            uint8_t enable_user_password;
+                            uint8_t delete_user_password;
+                        };
+                    };
+                    uint8_t temporary_admin_password[25];
+
+                    std::string dissect() const {
+                      std::stringstream ss;
+                      ss << "numlock:\t" << (int)numlock << std::endl;
+                      ss << "capslock:\t" << (int)capslock << std::endl;
+                      ss << "scrolllock:\t" << (int)scrolllock << std::endl;
+                      ss << "enable_user_password:\t" << (bool) enable_user_password << std::endl;
+                      ss << "delete_user_password:\t" << (bool) delete_user_password << std::endl;
+                      return ss.str();
+                    }
+                } __packed;
+
+                typedef Transaction<command_id(), struct CommandPayload, struct EmptyPayload>
+                    CommandTransaction;
+            };
         }
     }
 }
diff --git a/unittest/test3.cc b/unittest/test3.cc
index 6fab862..226e35c 100644
--- a/unittest/test3.cc
+++ b/unittest/test3.cc
@@ -16,15 +16,13 @@ const char * RFC_SECRET = "12345678901234567890";
 #include <NitrokeyManager.h>
 #include "device_proto.h"
 #include "log.h"
-#include "stick10_commands.h"
 #include "stick10_commands_0.8.h"
 //#include "stick20_commands.h"
 
 using namespace std;
 using namespace nitrokey::device;
 using namespace nitrokey::proto;
-//using namespace nitrokey::proto::stick10_08;
-using namespace nitrokey::proto::stick10;
+using namespace nitrokey::proto::stick10_08;
 using namespace nitrokey::log;
 using namespace nitrokey::misc;
 
@@ -39,6 +37,11 @@ void authorize(Stick10 &stick) {
   strcpy((char *) (authreq.card_password), default_admin_pin);
   strcpy((char *) (authreq.temporary_password), temporary_password);
   FirstAuthenticate::CommandTransaction::run(stick, authreq);
+
+  auto user_auth = get_payload<UserAuthenticate>();
+  strcpyT(user_auth.temporary_password, temporary_password);
+  strcpyT(user_auth.card_password, default_user_pin);
+  UserAuthenticate::CommandTransaction::run(stick, user_auth);
 }
 
 TEST_CASE("write slot", "[pronew]"){
@@ -46,7 +49,6 @@ TEST_CASE("write slot", "[pronew]"){
   connect_and_setup(stick);
 
   auto p = get_payload<stick10_08::WriteToHOTPSlot>();
-//  p.slot_number = 0 + 0x10;
   strcpyT(p.slot_secret, RFC_SECRET);
   strcpyT(p.temporary_admin_password, temporary_password);
   p.use_8_digits = true;
@@ -71,8 +73,65 @@ TEST_CASE("erase slot", "[pronew]"){
   connect_and_setup(stick);
   authorize(stick);
 
+  auto p3 = get_payload<GetHOTP>();
+  p3.slot_number = 0 + 0x10;
+  GetHOTP::CommandTransaction::run(stick, p3);
+
   auto erase_payload = get_payload<stick10_08::EraseSlot>();
-  erase_payload.slot_number = 1 + 0x10;
+  erase_payload.slot_number = 0 + 0x10;
   strcpyT(erase_payload.temporary_admin_password, temporary_password);
   stick10_08::EraseSlot::CommandTransaction::run(stick, erase_payload);
+
+  auto p4 = get_payload<GetHOTP>();
+  p4.slot_number = 0 + 0x10;
+  REQUIRE_THROWS(
+      GetHOTP::CommandTransaction::run(stick, p4)
+  );
+}
+
+TEST_CASE("write general config", "[pronew]") {
+  Stick10 stick;
+  connect_and_setup(stick);
+  authorize(stick);
+
+  auto p = get_payload<WriteGeneralConfig>();
+  p.enable_user_password = 1;
+  REQUIRE_THROWS(
+      WriteGeneralConfig::CommandTransaction::run(stick, p);
+  );
+  strcpyT(p.temporary_admin_password, temporary_password);
+  WriteGeneralConfig::CommandTransaction::run(stick, p);
+}
+
+TEST_CASE("authorize user OTP", "[pronew]") {
+  Stick10 stick;
+  connect_and_setup(stick);
+  authorize(stick);
+
+  auto p = get_payload<WriteGeneralConfig>();
+  p.enable_user_password = 1;
+  strcpyT(p.temporary_admin_password, temporary_password);
+  WriteGeneralConfig::CommandTransaction::run(stick, p);
+
+  auto pw = get_payload<WriteToHOTPSlot>();
+  strcpyT(pw.slot_secret, RFC_SECRET);
+  strcpyT(pw.temporary_admin_password, temporary_password);
+  pw.use_8_digits = true;
+  WriteToHOTPSlot::CommandTransaction::run(stick, pw);
+
+  auto pw2 = get_payload<WriteToHOTPSlot_2>();
+  strcpyT(pw2.temporary_admin_password, temporary_password);
+  pw2.slot_number = 0 + 0x10;
+  pw2.slot_counter = 0;
+  strcpyT(pw2.slot_name, "test name aaa");
+  WriteToHOTPSlot_2::CommandTransaction::run(stick, pw2);
+
+  auto p3 = get_payload<GetHOTP>();
+  p3.slot_number = 0 + 0x10;
+  REQUIRE_THROWS(
+      GetHOTP::CommandTransaction::run(stick, p3);
+  );
+  strcpyT(p3.temporary_user_password, temporary_password);
+  GetHOTP::CommandTransaction::run(stick, p3);
+
 }
\ No newline at end of file
-- 
cgit v1.2.3