taler-xotp_fw

xOTP generator firmware
Log | Files | Refs | Submodules | README

commit 6f2b51ef6cdd05169c2326f802a48a06e8e99d5a
parent 5e42ba78a42d47e00d8706b7cfcbb45651a6d7ed
Author: Andreas HABEGGER <andreas.habegger@bfh.ch>
Date:   Thu, 16 Oct 2025 16:50:37 +0200

Migration from GitLab and adaptation of directory structure.

Diffstat:
A.gitmodules | 9+++++++++
Athird_party/baseX-Converter | 1+
Athird_party/pbmLib | 1+
Athird_party/tinytime | 1+
Axotp_app/.gitignore | 3+++
Axotp_app/app/inc/frontend.h | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/talerAmount.h | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotp.h | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotpConfig.h | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotpCryptoHandler.h | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotpRegisterSecret.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotpSettings.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotpStateMachine.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/inc/xtotpUtil.h | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/frontend.c | 257+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/talerAmount.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/xtotp.c | 331+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/xtotpCryptoHandler.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/xtotpRegisterSecret.c | 564+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/xtotpSettings.c | 551+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/xtotpStateMachine.c | 726+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/app/src/xtotpUtil.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/autoPayInterface.h | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/batMeasInterface.h | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/clockInterface.h | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/cryptoInterface.h | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/deviceInterface.h | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/displayInterface.h | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/inputInterface.h | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/inc/syncInterface.h | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/autoPayInterface.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/batMeasInterface.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/clockInterface.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/cryptoInterface.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/deviceInterface.c | 40++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/displayInterface.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/inputInterface.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/interfaces/src/syncInterface.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/utils/inc/byteOrder.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/utils/inc/logger.h | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/utils/inc/ringbuffer.h | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/utils/src/byteOrder.c | 36++++++++++++++++++++++++++++++++++++
Axotp_app/utils/src/logger.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/utils/src/ringbuffer.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axotp_app/xtotp_app.make | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
45 files changed, 5872 insertions(+), 0 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "third_party/baseX-Converter"] + path = third_party/baseX-Converter + url = https://github.com/steinerAdi/baseX-Converter.git +[submodule "third_party/tinytime"] + path = third_party/tinytime + url = https://github.com/steinerAdi/tinytime.git +[submodule "third_party/pbmLib"] + path = third_party/pbmLib + url = https://github.com/steinerAdi/pbmLib.git diff --git a/third_party/baseX-Converter b/third_party/baseX-Converter @@ -0,0 +1 @@ +Subproject commit 97d9a88b64134a7a5b705c80491c58102670feda diff --git a/third_party/pbmLib b/third_party/pbmLib @@ -0,0 +1 @@ +Subproject commit 384e9c0fe2bece55b2f19028d4c1673c1a8596d3 diff --git a/third_party/tinytime b/third_party/tinytime @@ -0,0 +1 @@ +Subproject commit b4a00833a1ee84d73c767fc873858b80f00d6394 diff --git a/xotp_app/.gitignore b/xotp_app/.gitignore @@ -0,0 +1,2 @@ +version.h +doxyDoc +\ No newline at end of file diff --git a/xotp_app/app/inc/frontend.h b/xotp_app/app/inc/frontend.h @@ -0,0 +1,121 @@ +/** + * @file frontend.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Frontend designs for the corresponding state + * @version 0.1 + * @date 27-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#ifndef FRONTEND_H +#define FRONTEND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtotp.h" + +/** + * @brief Add data to the desired line position. + * The centered line use the big font, the other use the small font + * + */ +typedef enum { + FRONTEND_DATAFIELD_CENTRED = 0, + FRONTEND_DATAFIELD_LINE1, + FRONTEND_DATAFIELD_LINE2, + FRONTEND_MAX_DATAFIELD +} frontend_DataFieldPos; + +/** + * @brief Clears the full frontend image + * + * @param appData The application data handler + */ +void frontend_clear(xtotp_Data *appData); + +/** + * @brief Write the status icons and data on the top left corner + * + * @param appData The application data handler + + */ +void frontend_writeStatusIcons(const xtotp_Data *appData); + +/** + * @brief Clears the screen, add the status bar and write the menu title. + * In case of data is given, it is added in the centered big font to the display + * + * @param appData The application data handler + * @param menuTitle The title of the window + * @param data Possible data in the centered big font format + */ +void frontend_createBaseWindow(xtotp_Data *appData, + const char *menuTitle, + const char *data); + +/** + * @brief Updates the menu title on the top right side + * + * @param appData The application data handler + * @param menuTitle The title of the window + */ +void frontend_updateMenuTitle(xtotp_Data *appData, const char *menuTitle); + +/** + * @brief Add in the data field the given format data at the desired position + * + * @param appData The application data handler + * @param position The desired position + * - FRONTEND_DATAFIELD_CENTRED (big font) + * - FRONTEND_DATAFIELD_LINE1 (small font) + * - FRONTEND_DATAFIELD_LINE2 (small font) + * @param formats The data format and corresponding data + * @param ... data of format string + * @return uint32_t the starting y Position for the possibility to update only + * the window after this line + */ +uint32_t frontend_updateDatafield(xtotp_Data *appData, + frontend_DataFieldPos position, + const char *format, + ...); + +/** + * @brief Show the current secret states of the input number at the desired + * position + * + * @param appData The application data handler + */ +void frontend_showSecretState(xtotp_Data *appData); + +/** + * @brief Clears the datafield for the desired line + * + * @param appData The application data handler + * @param position The desired position + * - FRONTEND_DATAFIELD_CENTRED (big font) + * - FRONTEND_DATAFIELD_LINE1 (small font) + * - FRONTEND_DATAFIELD_LINE2 (small font) + */ +void frontend_clearDatafield(xtotp_Data *appData, + frontend_DataFieldPos position); + +#ifdef __cplusplus +} +#endif + +#endif /* FRONTEND_H*/ diff --git a/xotp_app/app/inc/talerAmount.h b/xotp_app/app/inc/talerAmount.h @@ -0,0 +1,145 @@ +/** + * @file talerAmount.h + * @author Adrian STEINER (steia19@bfh.ch) + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @brief Amount representation and utility function to handle the data. + * This file consists of parts of the file taler_amount_lib.h + * from the git repository https://git.gnunet.org/exchange.git. + * This is used to generate the same hash. + * The library cannot be used directly due to memory optimizations and MCU + * runtime capability. + * @version 0.1 + * @date 19-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *pbm_renderImage + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#ifndef TALER_AMOUNT_H +#define TALER_AMOUNT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtotpConfig.h" + +#include <stdbool.h> +#include <stdint.h> +/** + * @brief Number of bytes to store a secret. + * + */ +#define TALER_SECRET_LEN (20) + +/** + * @brief Max length of an base8 input of a secret + * + */ +#define TALER_SECRET_BASE8_LENGTH (((TALER_SECRET_LEN * 8) + 2) / 3) + +/** + * @brief Number of characters (plus 1 for 0-termination) we use to + * represent currency names (i.e. EUR, USD, etc.). We use 8+4 for + * alignment in the `struct TALER_Amount`. The amount is typically an + * ISO 4217 currency code when an alphanumeric 3-digit code is used. + * For regional currencies, the first character should be a "*" followed + * by a region-specific name (i.e. "*BRETAGNEFR"). + */ +#define TALER_CURRENCY_LEN (12) + +/** + * @brief The "fraction" value in a `struct TALER_Amount` represents which + * fraction of the "main" value. + * + * @note that we need sub-cent precision here as transaction fees might + * be that low, and as we want to support microdonations. + * + * An actual `struct Amount a` thus represents + * "a.value + (a.fraction / #TALER_AMOUNT_FRAC_BASE)" units of "a.currency". + */ +#define TALER_AMOUNT_FRAC_BASE (100000000) + +/** + * @brief How many digits behind the comma are required to represent the + * fractional value in human readable decimal format? Must match + * lg(#TALER_AMOUNT_FRAC_BASE). + */ +#define TALER_AMOUNT_FRAC_LEN (8) + +/** + * @brief Defines the maximum of digits to be calculated. + * + */ +#define TALER_MAX_PASSCODE_LENGTH (8) + +/** + * @brief Defines a packed attribute in a structure. + * Packed avoids padding bytes. + * + */ +#define PACKED __attribute__((packed)) + +/** + * @brief Amount, encoded for network transmission. + */ +typedef struct { + uint64_t value PACKED; ///< Value in the main currency, in NBO. */ + uint32_t fraction PACKED; ///< Fraction (integer multiples of + ///< #TALER_AMOUNT_FRAC_BASE), in NBO. + uint8_t + currency[TALER_CURRENCY_LEN]; ///< Type of the currency being represented. +} TALER_AmountNBO; + +/** + * @brief Taler extended data for xTOTP and autopay + * + */ +typedef struct { + uint8_t currency[TALER_CURRENCY_LEN]; ///< Currency + uint32_t fraction; ///< Fraction of currency + uint8_t merchantTemplateName[XTOTP_MERCHANT_TEMPLATE_SIZE]; ///< Merchant + ///< template name +} TALER_xData; + +/** + * @brief Convert the amount to the needed @ref TALER_AmountNBO to calculate the + * xTOTP passcode. + * + * @param target The target @ref TALER_AmountNBO structure + * @param amountInfo xData reference to get the currency and the fraction of it + * @param enteredAmount The entered amount to convert + */ +void TALER_amount2AmountNBO(TALER_AmountNBO *target, + const TALER_xData *amountInfo, + const uint64_t enteredAmount); + +/** + * @brief Initialise a @ref TALER_xDATA structure + * + * @param dataHandler The data structure to initlialise + * @param currency The currency to add + * @param fraction The fraction of the currency (e.g. 100 for Euro/CHF/Dollar) + * @param templateName Linked template for auto paying, can be set or not(use + * NULL or empty string) + */ +void TALER_xDataInit(TALER_xData *dataHandler, + const uint8_t *currency, + uint32_t fraction, + const uint8_t *templateName); +#ifdef __cplusplus +} +#endif + +#endif /* TALER_AMOUNT_H*/ diff --git a/xotp_app/app/inc/xtotp.h b/xotp_app/app/inc/xtotp.h @@ -0,0 +1,222 @@ +/** + * @file xtotp.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief xTOTP application data and interface for initialising and processing + * @version 0.1 + * @date 13-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef XTOTP_H +#define XTOTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "autoPayInterface.h" +#include "batMeasInterface.h" +#include "clockInterface.h" +#include "cryptoInterface.h" +#include "deviceInterface.h" +#include "displayInterface.h" +#include "inputInterface.h" +#include "syncInterface.h" + +/** + * @brief Base state-machine states + * + */ +typedef enum { + XTOTP_SLEEP = 0, + XTOTP_STARTUP, + XTOTP_DISPATCHER, + XTOTP_ENTER_AMOUNT, + XTOTP_AUTOPAY_START, + XTOTP_AUTOPAY_WAIT_RESPONSE, + XTOTP_AUTOPAY_VERIFY, + XTOTP_SHOW_XTOTP, + XTOTP_SHOW_TOTP, + XTOTP_GO_TO_SETTINGS, + XTOTP_SETTINGS, + XTOTP_MAX_STATES +} xtotp_BaseStates; + +/** + * @brief Device settings states + * + */ +typedef enum { + SETTINGS_ENTRY = 0, + SETTINGS_SELECT_KEY, + SETTINGS_REGISTER_KEY, + SETTINGS_ADDITIONAL, + SETTINGS_MAX_STATES +} xtotp_SettingStates; + +/** + * @brief Additional settings states + * + */ +typedef enum { + ADDITIONAL_CLOCK = 0, + ADDITIONAL_BATTERY_STATE = 1, + ADDITIONAL_LAST_SYNC_TIME = 2, + ADDITIONAL_DIFFERENCE_SYNC = 3, + ADDITIONAL_TIMEOUT_TIME = 4, + ADDITIONAL_SCREEN_MODE = 5, + ADDITIONAL_DEVICE_ID = 6, + ADDITIONAL_UNUSED_7 = 7, + ADDITIONAL_UNSUED_8 = 8, + ADDITIONAL_FIRMWARE = 9, + ADDITIONAL_MAX_STATES +} xtotp_AdditionalSettingStates; + +/** + * @brief Register secret states + * + */ +typedef enum { + XTOTP_REGISTER_SECRET_DISPATCHER = 0, + XTOTP_REGISTER_SECRET_SELECT, + XTOTP_REGISTER_SECRET_SET_ALGORITHM, + XTOTP_REGISTER_SECRET_SET_SECRET, + XTOTP_REGISTER_SECRET_SET_CODE_LENGTH, + XTOTP_REGISTER_SECRET_SET_INTERVAL, + XTOTP_REGISTER_SECRET_SET_CURRENCY, + XTOTP_REGISTER_SECRET_SET_FRACTION, + XTOTP_REGISTER_SECRET_MAX_STATES +} xtotp_RegisterSecretStates; + +/** + * @brief Structure of used state machine states + * + */ +typedef struct { + xtotp_BaseStates base; ///< Base state machine state + xtotp_SettingStates settings; ///< Settings state + xtotp_AdditionalSettingStates + additionalSettings; ///< Additional settings state + xtotp_RegisterSecretStates registerSecret; ///< Register secret state +} xtotp_States; + +/** + * @brief Process data during runtime + * + */ +typedef struct { + uint64_t enteredAmount; ///< Stores the current entered amount + uint32_t lastCode; ///< Saves the last generated code + uint64_t + lastNeededTime; ///< Stores the last needed time to reduce clock reading + bool updateDisplayData; ///< Flag to force a possible updated data field (i.e. + ///< for settings/information windows) +} xtotp_ProcessData; + +/** + * @brief xTOTP Generator data + * Stores all data centralized in one structure. + * + */ +typedef struct { + /* State machine states*/ + xtotp_States + currentStates; ///< Saves the current states of the state machines + /* Control Data */ + uint32_t + timeOutTime; ///< Increment the unused time to shutdown for power saving + uint32_t stopTimeEnterButton; ///< Increment the time the stop button is + ///< pressed to shutdown the device + bool secFlag; + /* TOTP data*/ + xtotp_ProcessData processData; ///< Saves the processing data + xtotpCryptoHandler cryptoHandler; ///< Handles the crypto data + /* Cryptographic*/ + cryptoInterface iCrypto; ///< Crypto interface to hash (x)TOTP functions + /* Device Interface */ + deviceInterface iDevice; ///< Device interface for system functions + /* HW Interface*/ + autoPayInterface + iAutoPay; ///< Interface for automated paying with given hardware + displayInterface iDisplay; ///< Interface for the display + inputInterface iInput; ///< Interface for the keyboard + clockInterface iClock; ///< Interface for the real time clock + syncInterface iSync; ///< Interface for synchronizing the time + batMeasInterface iBatMeas; ///< Interface for battery measurement +} xtotp_Data; + +/** + * @brief Initialise the xtotp_Data structure and link all used callback + * functions. + * + */ +uint8_t xtotp_init(xtotp_Data *appData, + /* Crypto interface */ + cryptoCalcTotpCB computeTotpCB, + cryptoCalcXTotpCB computeXTotpCB, + /* Device interface */ + deviceGetTickCB getTickCB, + deviceShutdownCB shutdownCB, + /* auto pay*/ + autoPayPwrCB autoPayPowerOnCB, + autoPayPwrCB autoPayPowerOffCB, + autoPayGetPasscodes autoPayGetPasscodesCB, + autoPayArePasscodesReceived autoPayArePasscodesReceivedCB, + autoPaySendURL autoPaySendUrlCB, + autoPayIsURLRead autoPayIsURLReadCB, + /* display interface */ + pbm_image *displayImage, + pbm_font *smallFont, + pbm_font *bigFont, + displayPwrCB displayPowerOnCB, + displayPwrCB displayPowerOffCB, + displayShowCB dispShowCB, + uint8_t symbolCharge, + uint8_t symbolSynchronize, + uint8_t symbolBatteryStart, + /* input interface */ + inputPwrCB inputPowerOnCB, + inputPwrCB inputPowerOffCB, + inputSampleCB inputReadInputCB, + /* clock interface */ + clockPwrCB clockPowerOnCB, + clockPwrCB clockPowerOffCB, + clockGetTimeCB clockGetUnixTimeCB, + clockSetTimeCB clockSetUnixTimeCB, + /* sync interface */ + syncPwrCB syncPowerOnCB, + syncPwrCB syncpowerOffCB, + synchronizeCB syncCB, + /* bat meas interface */ + batMeasPwrCB batMeasPowerOnCB, + batMeasPwrCB batMeasPowerOffCB, + getBatteryStateCB batMeasGetStateCB); + +/** + * @brief Process the state machine of the device. + * Must be called faster than than @ref XTOTP_SAMPLE_TIME + * + * @param appData The user data + * @return uint8_t EXIT_SUCCES in success, EXIT_FAILURE otherwise + */ +uint8_t xtotp_process(xtotp_Data *appData); + +#ifdef __cplusplus +} +#endif + +#endif /* XTOTP_H*/ diff --git a/xotp_app/app/inc/xtotpConfig.h b/xotp_app/app/inc/xtotpConfig.h @@ -0,0 +1,121 @@ +/** + * @file xtotpConfig.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Configuration file to set default values and buffer sizes. + * It is used that a user can redefine this values and configure their own + * application for his device. + * @version 0.1 + * @date 01-08-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef XTOTP_CONFIG_H +#define XTOTP_CONFIG_H + +#include "inputInterface.h" +#include "pbm_types.h" +#include "version.h" ///< Generated with a Makefile target with data from the git repository +#include "xtotpUtil.h" + +/* ---------- Synch data: ----------*/ +/* Synchronize periodic in seconds */ +#ifndef XTOTP_SYNC_UPDATE_TIME +#define XTOTP_SYNC_UPDATE_PERIOD \ + (90 * 24 * 60 * 60) ///< 90 days (day * hour * min * sec) +#endif +/* Synchronize error time in ms*/ +#ifndef XTOTP_SYNC_ERROR_RESET +#define XTOTP_SYNC_ERROR_RESET \ + (3 * APP_SEC_BASE) ///< Error reset for synchronisation in ms +#endif + +#ifndef XTOTP_SYNC_MIN_BATTERY_LEVEL +#define XTOTP_SYNC_MIN_BATTERY_LEVEL \ + (5) ///< Minimum battery state in percent to synchronize +#endif + +#ifndef XTOTP_SYNC_MAX_SYNC_TIME +#define XTOTP_SYNC_MAX_SYNC_TIME \ + (30 * 60 * APP_SEC_BASE) ///< Maximum time to sync until force a newStart in s +#endif + +/* ---------- Sample time in ms ----------*/ +#ifndef XTOTP_SAMPLE_TIME +#define XTOTP_SAMPLE_TIME (10) ///< Input sample time in ms +#endif + +/* ---------- DEFAULT CONFIGURATION VALUES ---------- */ + +/* */ +#ifndef XTOTP_STOP_TIME_BUTTON +#define XTOTP_STOP_TIME_BUTTON \ + (3 * APP_SEC_BASE) ///< Time to press the shutdown button (3000 ms) +#endif + +#ifndef XTOTP_DEFAULT_TIMEOUT +#define XTOTP_DEFAULT_TIMEOUT \ + (2 * 60 * APP_SEC_BASE) ///< Timeout time to save energy (2min) +#endif + +#ifndef XTOTP_MAX_TIMEOUT_TIME +#define XTOTP_MAX_TIMEOUT_TIME (999) ///< Maximum timeout time +#endif + +#ifndef XTOTP_DEFAULT_THEME +#define XTOTP_DEFAULT_THEME (PBM_BLACK) ///< Default font color +#endif + +#ifndef XTOTP_BATTERY_CHECK_TIME +#define XTOTP_BATTERY_CHECK_TIME \ + (1 * 60 * APP_SEC_BASE) ///< Time to force a battery measurement +#endif + +#ifndef XTOTP_REACTION_EDGE +#define XTOTP_REACTION_EDGE (INPUT_STATE_RISING_EDGE) ///< Default edge reaction +#endif + +/* ----------- xTOTP and autopay settings --------- */ +#ifndef XTOTP_STORABLE_SECRETS +#define XTOTP_STORABLE_SECRETS (10) ///< Storable secrets in the device +#endif + +#ifndef XTOTP_DEVICE_ID +#define XTOTP_DEVICE_ID ("xTOTP") ///< Initialised device ID +#endif + +#ifndef XTOTP_DEVICE_ID_SIZE +#define XTOTP_DEVICE_ID_SIZE (12) ///< Max size of device id +#endif + +#ifndef XTOTP_MERCHANT_BACKEND +#define XTOTP_MERCHANT_BACKEND \ + ("") ///< Merchant backend default for automated paying +#endif + +#ifndef XTOTP_MERCHANT_BACKEND_SIZE +#define XTOTP_MERCHANT_BACKEND_SIZE (96) ///< Merchant backend buffer size +#endif + +#ifndef XTOTP_MERCHANT_TEMPLATE_SIZE +#define XTOTP_MERCHANT_TEMPLATE_SIZE (32) ///< Merchant template name size +#endif + +#ifndef XTOTP_AUTOPAY_RECEIVED_PASSKEYS +#define XTOTP_AUTOPAY_RECEIVED_PASSKEYS (5) ///< Received passkey with autopay +#endif + +#endif /*XTOTP_CONFIG_H*/ +\ No newline at end of file diff --git a/xotp_app/app/inc/xtotpCryptoHandler.h b/xotp_app/app/inc/xtotpCryptoHandler.h @@ -0,0 +1,164 @@ +/** + * @file xtotpCryptoHandler.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Handler the (x)TOTP crypto data and generates the current codes + * @version 0.1 + * @date 03-08-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef XTOTP_CRYPTO_HANDLER_H +#define XTOTP_CRYPTO_HANDLER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "talerAmount.h" +#include "xtotpConfig.h" + +#define CRYPTO_NEW_SECRET_SEGMENT (0) /// Position of new registered secret + +/** + * @brief Crypto algorithmen + * + */ +typedef enum { + ALG_NONE = 0, ///< No algortithm set + ALG_TOTP_SHA1, ///< TOTP-SHA1 + ALG_MAX_TOTP_ALGORITHM, ///< Max TOTP algorithms + ALG_CHANGE_VALUE = 0x10, ///< Change value for xTOTP algs + ALG_XTOTP_SHA1, ///< xTOTP-SHA1 + ALG_MAX_XTOTP_ALGORITHM ///< Max xTOTP algorithms +} crypto_algorithms; + +/** + * @brief Crypto algorithm info type + * + */ +typedef enum { + ALGO_UNINITIALIZED = 0, ///< Uninitialised algorithm + ALGO_TOTP, ///< Base TOTP algorithm + ALGO_XTOTP ///< extendend TOTP algorithm +} crypto_algoInfo; + +/** + * @brief Secret basis to register + * + */ +typedef enum { + SECRET_BASE_BYTE = 0, ///< Secret interpreted as byte + SECRET_BASE_8, ///< Secret interpreted as base-8 representation + SECRET_BASE_32 ///< Secret interpreted as base-32 representation +} crypto_secretBase; + +/** + * @brief Includes all needed data to create an (x)TOTP passcode + * + */ +typedef struct { + uint8_t secret[TALER_SECRET_LEN]; ///< Secret + uint8_t secretLen; ///< Secret length + uint8_t interval; ///< Interval in s + uint8_t digits; ///< Number of digits + crypto_algorithms algorithm; ///< Used algorithm + TALER_xData xTalerData; ///< Additiontal data for extended(x)TOTP generation +} xtotpAlgoSettingsType; + +/** + * @brief Crypto handler structure + * + */ +typedef struct { + uint8_t usedAlgorithm; ///< Current used secret + uint8_t processedAlgorithm; ///< Selected secret to change/add + char + deviceID[XTOTP_DEVICE_ID_SIZE]; ///< Device ID named in the taler merchant + char merchandBackend[XTOTP_MERCHANT_BACKEND_SIZE]; ///< Merchant backend link + xtotpAlgoSettingsType + algorithms[XTOTP_STORABLE_SECRETS + 1]; ///< Algorithms and one config +} xtotpCryptoHandler; + +/** + * @brief Get algorithm algorithm information + * + * @param algoInfo The algorithm settings + * @return crypto_algoInfo algorithm info + */ +crypto_algoInfo crypto_getAlgoInfo(xtotpAlgoSettingsType *algoSettings); + +/** + * @brief Get algorithm as text linked to a constant c-string array + * + * @param algoInfo The algorithm settings + * @return const char* name of algorithm + */ +const char *crypto_getAlgoName(xtotpAlgoSettingsType *algoSettings); + +/** + * @brief initialise a new algorithm with the given settings + * + * @param algoSettings The structure to initialise + * @param secret The secret + * @param secretLen Length of the secret + * @param secretBase Secret base of @ref crypto_secretBase + * @param interval Valid interval time + * @param passcodeDigits Number of digits to display + * @param algorithm The used algorithm from @ref crypto_algorithms + * @param currency The used currency, unused in case of a base TOTP algorithm + * @param fraction The fraction of the currency, unused in case of a base TOTP + * algorithm + * @param merchantTemplate Template name, used for auto-paying. Unused in case + * of a base TOTP algorithm or not used auto-pay function + * @return crypto_algoInfo The crypto information, ALGO_UNINITIALIZED in case of + * an error + */ +crypto_algoInfo crypto_initAlgoSettings(xtotpAlgoSettingsType *algoSettings, + const uint8_t *secret, + uint8_t secretLen, + crypto_secretBase secretBase, + uint8_t interval, + uint8_t passcodeDigits, + crypto_algorithms algorithm, + const uint8_t *currency, + uint32_t fraction, + const uint8_t *merchantTemplate); + +/** + * @brief Sets in the crypto handler the merchant data + * + * @param cryptoHandler The crypto handler + * @param deviceID Device ID or NULL to not change + * @param merchantBackend Merchant backend URL or NULL to not change + */ +void crypto_setMerchantData(xtotpCryptoHandler *cryptoHandler, + const char *deviceID, + const char *merchantBackend); + +/** + * @brief Get the current used algorithm + * + * @param cryptoHandler The crypto handler + * @return xtotpAlgoSettingsType* pointer to the used crypto algorithm + */ +xtotpAlgoSettingsType * +crypto_getCurrentAlgorithm(xtotpCryptoHandler *cryptoHandler); +#ifdef __cplusplus +} +#endif + +#endif /* XTOTP_CRYPTO_HANDLER_H */ diff --git a/xotp_app/app/inc/xtotpRegisterSecret.h b/xotp_app/app/inc/xtotpRegisterSecret.h @@ -0,0 +1,49 @@ +/** + * @file xtotpRegisterSecret.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Settings state machine to register new secret with a corresponding + * althorihm Changing and selecting is as well intergrated in this module + * @version 0.1 + * @date 24-07-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef XTOTP_REGISTER_SECRET_H +#define XTOTP_REGISTER_SECRET_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtotp.h" + +/** + * @brief Process the selection or registration of a algorithm with a new secret + * + * @param appData + * @param inputState + * @param inputButton + */ +void settings_processSecretHandling(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +#ifdef __cplusplus +} +#endif + +#endif /* XTOTP_REGISTER_SECRET_H*/ diff --git a/xotp_app/app/inc/xtotpSettings.h b/xotp_app/app/inc/xtotpSettings.h @@ -0,0 +1,47 @@ +/** + * @file xtotpSettings.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Settings state machine for secret registration and additional settings + * @version 0.1 + * @date 27-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#ifndef XTOTP_SETTINGS_H +#define XTOTP_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtotp.h" + +/** + * @brief Process the additional + * + * @param appData + * @param inputState + * @param inputButton + */ +void settings_processAdditional(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +#ifdef __cplusplus +} +#endif + +#endif /* XTOTP_SETTINGS_H*/ diff --git a/xotp_app/app/inc/xtotpStateMachine.h b/xotp_app/app/inc/xtotpStateMachine.h @@ -0,0 +1,70 @@ +/** + * @file xtotpStateMachine.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief State machine of the totp device + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#ifndef XTOTP_STATE_MACHINE_H +#define XTOTP_STATE_MACHINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xtotp.h" + +#include "inputInterface.h" + +/** + * @brief State callback protoype + * + * @param appData the user data + * @param inputState the current input state + * @param inputButton the current input button position + * + */ +typedef void (*stateCB)(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +/** + * @brief State machine handler + * + */ +typedef struct { + stateCB entryAction; + stateCB runAction; +} stateHandler; + +/** + * @brief Process the state machine + * + * @param appData User and app data + * @param inputState Input state (low/high) + * @param inputButton Input button + */ +void xtotp_processStateMachine(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +#ifdef __cplusplus +} +#endif + +#endif /* XTOTP_STATE_MACHINE_H*/ diff --git a/xotp_app/app/inc/xtotpUtil.h b/xotp_app/app/inc/xtotpUtil.h @@ -0,0 +1,102 @@ +/** + * @file xtotpUtil.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Includes some help functions and important global defines + * @version 0.1 + * @date 27-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#ifndef XTOTP_UTIL_H +#define XTOTP_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#define MAX_AMOUNT_DIGITS (8) ///< Maximum 8 digits for digits +#define MAX_INPUT_LENGTH (10000000) ///< Maximum number for the input (8 DIGITS) +#define MAX_PASSCODE_LENGTH_DIGITS (8) ///< Maximum passcode length +#define MAX_PASSCODE_LENGTH_NUMBER (100000000) ///< Maximum passcode length + +#define PAYMENT_BASE (10) ///< Payment is based on 10 + +#define APP_SEC_BASE (1000) ///< Second in the base (ms) +#define MS_TO_SECOND(s) \ + ((s) / APP_SEC_BASE) ///< Get the second value of a millisecond +#define SECOND_TO_MS(s) ((s) * APP_SEC_BASE) ///< Get the ms value of a second + +/** + * @brief Get the minium of a and b + * + */ +#define XTOTP_MIN(A, B) ((A) < (B) ? (A) : (B)) + +/** + * @brief Get the maximum of a and b + * + */ +#define XTOTP_MAX(A, B) ((A) > (B) ? (A) : (B)) + +/** + * @brief Check if the data is in range + * + */ +#define XTOTP_IS_IN_RANGE(X, MIN, MAX) \ + ((((X) >= (MIN)) && ((X) <= (MAX))) ? (true) : (false)) + +/** + * @brief Get the array size + * + */ +#define XTOTP_ARRAY_SIZE(ARRAY) (sizeof((ARRAY)) / sizeof((ARRAY[0]))) + +/** + * @brief Calculates the power of the base 10 with the desired exponent + * + * @param exponent A positive integer as exponent. Accepted values are 0-9 + * (to hide an overflow) + * @return uint32_t The result of the calculation + */ +uint32_t power10(const uint8_t exponent); + +/** + * @brief Returns the base-10 exponent of a positive integer. + * + * Calculates how many digits the number has. + * + * @param number A positive integer. + * @return uint8_t The number of digits in base 10. + */ + +uint8_t base10Exponent(uint32_t number); + +/** + * @brief Calculates the tick difference including an overflow of the new tick + * + * @param newTick The current tick + * @param oldTick The old tick for the difference + * @return uint32_t the difference including overflow error + */ +uint32_t getTickDiff(uint32_t newTick, uint32_t oldTick); + +#ifdef __cplusplus +} +#endif + +#endif /* XTOTP_UTIL_H*/ diff --git a/xotp_app/app/src/frontend.c b/xotp_app/app/src/frontend.c @@ -0,0 +1,256 @@ +/** + * @file frontend.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Frontend source code to set the desired display output + * @version 0.1 + * @date 27-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#include "frontend.h" + +#include "pbm_graphics.h" +#include "pbm_types.h" + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "logger.h" +#include "xtotpConfig.h" +#include "xtotpUtil.h" + +#define BORDER_TOP (1) +#define BORDER_LEFT (1) +#define BORDER_RIGHT (1) +#define LINE_BORDER (1) +#define LINE_SIZE (1) + +char bufferSmall[64]; +char bufferBig[64]; + +void frontend_clear(xtotp_Data *appData) +{ + pbm_fill(appData->iDisplay.image, !appData->iDisplay.theme); +} + +void frontend_writeStatusIcons(const xtotp_Data *appData) +{ +#define BATTERY_SYMBOL_DELTA (4U) ///< 5 Levels of battery symbols are used +#define BATTERY_FULL (100U) ///< 100% as maximum state + uint8_t chargeLevel = batMeas_getBatteryPercentState(&appData->iBatMeas); + char chargeSymbol = 0; + switch (appData->iBatMeas.chargingStatus) { + case BAT_FULL: + chargeSymbol = appData->iDisplay.symbolBatteryStart; + break; + case BAT_DISCHARGE: + case BAT_CHARGING: + chargeSymbol = + (chargeLevel == 0) + ? appData->iDisplay.symbolBatteryStart + BATTERY_SYMBOL_DELTA + : appData->iDisplay.symbolBatteryStart + + ((BATTERY_SYMBOL_DELTA - 1) - + ((chargeLevel - 1) / (BATTERY_FULL / BATTERY_SYMBOL_DELTA))); + break; + case BAT_UNKNOWN: + chargeSymbol = '?'; + break; + + default: + break; + } + + snprintf(bufferSmall, sizeof(bufferSmall), "%c%3u%%%c%c", chargeSymbol, + chargeLevel, + ((BAT_CHARGING == appData->iBatMeas.chargingStatus || + BAT_FULL == appData->iBatMeas.chargingStatus) + ? appData->iDisplay.symbolCharge + : ' '), + ((appData->iSync.isSynchronizing) + ? appData->iDisplay.symbolSynchronize + : ' ')); + + pbm_writeString(appData->iDisplay.image, BORDER_LEFT, BORDER_TOP, + appData->iDisplay.theme, appData->iDisplay.dispFontSmall, + PBM_STRING_LEFT_TOP, bufferSmall); +} + +void frontend_updateMenuTitle(xtotp_Data *appData, const char *menuTitle) +{ + if (NULL == appData || NULL == menuTitle) { + return; + } + pbm_writeString(appData->iDisplay.image, + appData->iDisplay.image->width - BORDER_RIGHT, BORDER_TOP, + appData->iDisplay.theme, appData->iDisplay.dispFontSmall, + PBM_STRING_RIGHT_TOP, menuTitle); +} + +uint32_t frontend_updateDatafield(xtotp_Data *appData, + frontend_DataFieldPos position, + const char *format, + ...) +{ + /* Create string */ + va_list args; + va_start(args, format); + uint32_t baseYPos = BORDER_TOP + appData->iDisplay.dispFontSmall->height + + 2 * LINE_BORDER + LINE_SIZE; + frontend_clearDatafield(appData, position); + switch (position) { + case FRONTEND_DATAFIELD_CENTRED: + vsnprintf(bufferBig, sizeof(bufferBig), format, args); + va_end(args); + pbm_writeString(appData->iDisplay.image, + appData->iDisplay.image->width - BORDER_RIGHT, baseYPos, + appData->iDisplay.theme, appData->iDisplay.dispFontBig, + PBM_STRING_RIGHT_TOP, bufferBig); + break; + case FRONTEND_DATAFIELD_LINE1: + vsnprintf(bufferSmall, sizeof(bufferSmall), format, args); + va_end(args); + pbm_writeString(appData->iDisplay.image, + appData->iDisplay.image->width - BORDER_RIGHT, baseYPos, + appData->iDisplay.theme, appData->iDisplay.dispFontSmall, + PBM_STRING_RIGHT_TOP, bufferSmall); + break; + case FRONTEND_DATAFIELD_LINE2: + vsnprintf(bufferSmall, sizeof(bufferSmall), format, args); + va_end(args); + baseYPos += appData->iDisplay.dispFontSmall->height + LINE_SIZE; + pbm_writeString(appData->iDisplay.image, + appData->iDisplay.image->width - BORDER_RIGHT, baseYPos, + appData->iDisplay.theme, appData->iDisplay.dispFontSmall, + PBM_STRING_RIGHT_TOP, bufferSmall); + break; + default: + va_end(args); + return 0; + } + return baseYPos; +} + +void frontend_createBaseWindow(xtotp_Data *appData, + const char *menuTitle, + const char *data) +{ + frontend_clear(appData); + frontend_writeStatusIcons(appData); + frontend_updateMenuTitle(appData, menuTitle); + uint32_t linePos = + BORDER_TOP + appData->iDisplay.dispFontSmall->height + LINE_BORDER; + pbm_drawLine(appData->iDisplay.image, 0, linePos, + appData->iDisplay.image->width, linePos, + appData->iDisplay.theme); + if (data) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, data); + } +} + +void frontend_showSecretState(xtotp_Data *appData) +{ +#define MAX_DISPLAY_NUMBERS (11) +#define INPUT_AS_STRING_SIZE (4) + + uint32_t baseYPos = BORDER_TOP + appData->iDisplay.dispFontSmall->height + + 2 * LINE_BORDER + LINE_SIZE; + pbm_font *usedFont = appData->iDisplay.dispFontSmall; + pbm_colors usedColor = appData->iDisplay.theme; + uint32_t xPos = appData->iDisplay.image->width - BORDER_RIGHT; + uint32_t decimalNumber = (appData->cryptoHandler.processedAlgorithm * 10); + uint32_t unitPlace = appData->cryptoHandler.processedAlgorithm % 10; + frontend_clearDatafield(appData, FRONTEND_DATAFIELD_CENTRED); + // Show current input with checking the current input + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, + "Input secret: "); + char inputAsString[INPUT_AS_STRING_SIZE]; + snprintf(inputAsString, INPUT_AS_STRING_SIZE, "%u", + appData->cryptoHandler.processedAlgorithm); + if (crypto_getAlgoInfo( + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm]) != + ALGO_UNINITIALIZED) { + pbm_writeString(appData->iDisplay.image, xPos, baseYPos, !usedColor, + usedFont, PBM_STRING_RIGHT_TOP, inputAsString); + } else { + pbm_writeString(appData->iDisplay.image, xPos, baseYPos, usedColor, + usedFont, PBM_STRING_RIGHT_TOP, inputAsString); + } + baseYPos += appData->iDisplay.dispFontSmall->height + LINE_SIZE; + xPos -= usedFont->width; + if (XTOTP_STORABLE_SECRETS >= 10 && decimalNumber < XTOTP_STORABLE_SECRETS) { + // Display second line for decimal secrets + for (int8_t i = XTOTP_MIN(XTOTP_STORABLE_SECRETS - decimalNumber, 9); + i >= 0; i--) { + if (crypto_getAlgoInfo( + &appData->cryptoHandler.algorithms[i + decimalNumber]) != + ALGO_UNINITIALIZED) { + usedColor = !appData->iDisplay.theme; + } else { + usedColor = appData->iDisplay.theme; + } + pbm_writeChar(appData->iDisplay.image, xPos, baseYPos, usedColor, + usedFont, i + '0'); + xPos -= (usedFont->width); + pbm_writeChar(appData->iDisplay.image, xPos, baseYPos, usedColor, + usedFont, unitPlace + '0'); + xPos -= (usedFont->width + 1); + } + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "MAX: %3u ", XTOTP_STORABLE_SECRETS); + for (uint8_t i = XTOTP_MIN(XTOTP_STORABLE_SECRETS, 9); i > 0; i--) { + pbm_colors usedColor = appData->iDisplay.theme; + if (crypto_getAlgoInfo(&appData->cryptoHandler.algorithms[i]) != + ALGO_UNINITIALIZED) { + usedColor = !appData->iDisplay.theme; + } else { + usedColor = appData->iDisplay.theme; + } + pbm_writeChar(appData->iDisplay.image, xPos, baseYPos, usedColor, + usedFont, i + '0'); + xPos -= (usedFont->width + 1); + } + } +} + +void frontend_clearDatafield(xtotp_Data *appData, + frontend_DataFieldPos position) +{ + uint32_t startYPos = BORDER_TOP + appData->iDisplay.dispFontSmall->height + + 2 * LINE_BORDER + LINE_SIZE; + uint32_t endYPos = appData->iDisplay.image->height; + switch (position) { + case FRONTEND_DATAFIELD_CENTRED: + // Clear all, nothing to do + break; + case FRONTEND_DATAFIELD_LINE1: + // end position until line 2 + endYPos = appData->iDisplay.dispFontSmall->height; + break; + case FRONTEND_DATAFIELD_LINE2: + startYPos += appData->iDisplay.dispFontSmall->height; + break; + default: + return; + break; + } + for (uint32_t y = startYPos; y < endYPos; y++) { + pbm_drawLine(appData->iDisplay.image, 0, y, PBM_IMAGE_END, y, + !appData->iDisplay.theme); + } +} +\ No newline at end of file diff --git a/xotp_app/app/src/talerAmount.c b/xotp_app/app/src/talerAmount.c @@ -0,0 +1,67 @@ +/** + * @file talerAmount.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Help functions to handle the talerAmount data structures + * @version 0.1 + * @date 19-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "talerAmount.h" + +#include <stddef.h> +#include <string.h> + +#include "byteOrder.h" + +void TALER_amount2AmountNBO(TALER_AmountNBO *target, + const TALER_xData *amountInfo, + const uint64_t enteredAmount) +{ + if (NULL == target || NULL == amountInfo) { + return; + } + if (amountInfo->fraction > 0) { + uint64_t amountValue = enteredAmount / amountInfo->fraction; + uint32_t amountFraction = (enteredAmount % amountInfo->fraction) * + (TALER_AMOUNT_FRAC_BASE / amountInfo->fraction); + target->value = h2n64(amountValue); + target->fraction = h2n32(amountFraction); + } else { + target->value = h2n64(enteredAmount); + target->fraction = 0; + } + memcpy(target->currency, amountInfo->currency, TALER_CURRENCY_LEN); +} + +void TALER_xDataInit(TALER_xData *dataHandler, + const uint8_t *currency, + uint32_t fraction, + const uint8_t *templateName) +{ + if (NULL != dataHandler) { + strncpy((char *)dataHandler->currency, (const char *)currency, + TALER_CURRENCY_LEN); + dataHandler->fraction = fraction; + if (templateName) { + strncpy((char *)dataHandler->merchantTemplateName, (char *)templateName, + XTOTP_MERCHANT_TEMPLATE_SIZE); + } else { + dataHandler->merchantTemplateName[0] = '\0'; + } + } +} diff --git a/xotp_app/app/src/xtotp.c b/xotp_app/app/src/xtotp.c @@ -0,0 +1,330 @@ +/** + * @file xtotp.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief xTOTP application data and interface for initialising and processing + * @version 0.1 + * @date 13-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "xtotp.h" +#include <stdlib.h> + +#include "logger.h" + +#include "frontend.h" +#include "xtotpConfig.h" +#include "xtotpStateMachine.h" +#include "xtotpUtil.h" + +uint32_t oldTick = 0; +uint32_t stopTick = 0; +uint32_t timeoutTick = 0; + +/** + * @brief Function to sample the synchronizing data if enables + * + * @param appData Application data + * @param diffTick Tick difference to increment the sync time + */ +void sample_synchronize(xtotp_Data *appData, uint32_t diffTick); + +uint8_t xtotp_init( + /* app data*/ + xtotp_Data *appData, + /* Crypto interface */ + cryptoCalcTotpCB computeTotpCB, + cryptoCalcXTotpCB computeXTotpCB, + /* Device interface */ + deviceGetTickCB getTickCB, + deviceShutdownCB shutdownCB, + /* auto pay*/ + autoPayPwrCB autoPayPowerOnCB, + autoPayPwrCB autoPayPowerOffCB, + autoPayGetPasscodes autoPayGetPasscodesCB, + autoPayArePasscodesReceived autoPayArePasscodesReceivedCB, + autoPaySendURL autoPaySendUrlCB, + autoPayIsURLRead autoPayIsURLReadCB, + /* display interface */ + pbm_image *displayImage, + pbm_font *smallFont, + pbm_font *bigFont, + displayPwrCB displayPowerOnCB, + displayPwrCB displayPowerOffCB, + displayShowCB dispShowCB, + uint8_t symbolCharge, + uint8_t symbolSynchronize, + uint8_t symbolBatteryStart, + /* input interface */ + inputPwrCB inputPowerOnCB, + inputPwrCB inputPowerOffCB, + inputSampleCB inputReadInputCB, + /* clock interface */ + clockPwrCB clockPowerOnCB, + clockPwrCB clockPowerOffCB, + clockGetTimeCB clockGetUnixTimeCB, + clockSetTimeCB clockSetUnixTimeCB, + /* sync interface */ + syncPwrCB syncPowerOnCB, + syncPwrCB syncpowerOffCB, + synchronizeCB syncCB, + /* bat meas interface */ + batMeasPwrCB batMeasPowerOnCB, + batMeasPwrCB batMeasPowerOffCB, + getBatteryStateCB batMeasGetStateCB) +{ + + uint8_t returnValue = EXIT_SUCCESS; + + /* Initial states */ + appData->currentStates.base = XTOTP_STARTUP; + appData->currentStates.settings = SETTINGS_ENTRY; + appData->currentStates.additionalSettings = ADDITIONAL_CLOCK; + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER; + + appData->timeOutTime = XTOTP_DEFAULT_TIMEOUT; + appData->stopTimeEnterButton = XTOTP_STOP_TIME_BUTTON; + + appData->processData.enteredAmount = 0; + appData->processData.lastCode = 0; + appData->processData.lastNeededTime = 0; + + appData->secFlag = false; + + /* Crypto interface */ + if (crypto_initInterface(&appData->iCrypto, computeTotpCB, computeXTotpCB)) { + returnValue = EXIT_FAILURE; + } + + crypto_setMerchantData(&appData->cryptoHandler, XTOTP_DEVICE_ID, + XTOTP_MERCHANT_BACKEND); + + uint8_t secret[] = {0xdc, 0x73, 0x17, 0x1d, 0xc7, 0xd1, 0xfa, + 0x97, 0xa6, 0x8, 0xa, 0x8c, 0x74, 0x64, + 0xdf, 0x83, 0xda, 0x6b, 0x2a, 0x72}; + // uint8_t base8Secret[] = + // "132378488654568243857521365366371461664865781465232244"; + appData->cryptoHandler.usedAlgorithm = 1; + uint8_t secretBase32[] = "55IOFHFBZVFC7KTBD4XTN7SH5ZZTNLKS"; + crypto_initAlgoSettings( + &appData->cryptoHandler.algorithms[appData->cryptoHandler.usedAlgorithm], + secretBase32, sizeof(secretBase32), SECRET_BASE_32, 30, 8, ALG_XTOTP_SHA1, + (uint8_t *)"KUDOS", 100, (const uint8_t *)"xTOTP_test"); + + crypto_initAlgoSettings( + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.usedAlgorithm + 1], + secret, sizeof(secret), SECRET_BASE_BYTE, 30, 4, ALG_XTOTP_SHA1, + (uint8_t *)"KUDOS", 0, NULL); + + // uint8_t base8Secret[] = + // "132378488654568243857521365366371461664865781465232244"; + + uint8_t secret2[] = "BCW575RZPFV42CCWFNLA5C27WNYOYKEW"; + crypto_initAlgoSettings( + &appData->cryptoHandler.algorithms[XTOTP_STORABLE_SECRETS], secret2, + sizeof(secret2), SECRET_BASE_32, 60, 6, ALG_TOTP_SHA1, NULL, 0, NULL); + + /* Autopay interface */ + if (autoPay_initInterface(&appData->iAutoPay, autoPayPowerOnCB, + autoPayPowerOffCB, autoPayGetPasscodesCB, + autoPayArePasscodesReceivedCB, autoPaySendUrlCB, + autoPayIsURLReadCB)) { + returnValue = EXIT_FAILURE; + } + + /* Device interface */ + if (device_initInterface(&appData->iDevice, getTickCB, shutdownCB)) { + returnValue = EXIT_FAILURE; + } + + /* HW interface*/ + if (display_initInterface(&appData->iDisplay, displayImage, smallFont, + bigFont, displayPowerOnCB, displayPowerOffCB, + dispShowCB, symbolCharge, symbolSynchronize, + symbolBatteryStart, XTOTP_DEFAULT_THEME)) { + returnValue = EXIT_FAILURE; + } + if (input_initInterface(&appData->iInput, inputPowerOnCB, inputPowerOffCB, + inputReadInputCB)) { + returnValue = EXIT_FAILURE; + } + if (clock_initInterface(&appData->iClock, clockPowerOnCB, clockPowerOffCB, + clockGetUnixTimeCB, clockSetUnixTimeCB)) { + returnValue = EXIT_FAILURE; + } + + if (sync_initInterface(&appData->iSync, syncPowerOnCB, syncpowerOffCB, syncCB, + FIRMWARE_COMPILE_TIME)) { + returnValue = EXIT_FAILURE; + } + + if (batMeas_initInterface(&appData->iBatMeas, batMeasPowerOnCB, + batMeasPowerOffCB, batMeasGetStateCB)) { + returnValue = EXIT_FAILURE; + } + + if (returnValue) { + return returnValue; + } + + appData->iClock.powerOn(); + appData->iClock.setUnixTime(FIRMWARE_COMPILE_TIME); + appData->iClock.powerOff(); + + xtotp_process(appData); + return EXIT_SUCCESS; +} + +uint8_t xtotp_process(xtotp_Data *appData) +{ + uint32_t currentTick = appData->iDevice.getTick(); + uint32_t diffTick = getTickDiff(currentTick, oldTick); + if (currentTick % APP_SEC_BASE) { + appData->secFlag = true; + } + if (appData->currentStates.base == XTOTP_STARTUP) { + diffTick = XTOTP_SAMPLE_TIME; + timeoutTick = 0; + stopTick = 0; + } + + if (diffTick >= XTOTP_SAMPLE_TIME) { + oldTick = currentTick; + inputButtonPos buttonPos; + inputLevelState inputState = appData->iInput.readInput(&buttonPos); + LOGGER_TRACE("Pressed button %u with state %u", buttonPos, inputState); + + // Reset activity timeout + if (inputState == INPUT_STATE_RISING_EDGE || + inputState == INPUT_STATE_FALLING_EDGE) { + // Recognise activity + timeoutTick = 0; + } else { + timeoutTick += diffTick; + } + // Check timeout activity if enabled (time > 0) + if (appData->timeOutTime > 0 && timeoutTick >= appData->timeOutTime) { + // Timeout needed => shutdown + timeoutTick = 0; + appData->currentStates.base = XTOTP_SLEEP; + LOGGER_INFO("Shutdown during inactivity"); + } + + if (INPUT_ENTER == buttonPos) { + switch (inputState) { + case INPUT_STATE_RISING_EDGE: + stopTick = 0; + break; + case INPUT_STATE_HIGH: + stopTick += diffTick; + if (stopTick >= appData->stopTimeEnterButton) { + stopTick = 0; + LOGGER_INFO("Shutdown device due button pressing"); + appData->currentStates.base = XTOTP_SLEEP; + } + break; + default: + // Nothing to do + break; + } + } + + if (!batMeas_measure(&appData->iBatMeas, currentTick, + XTOTP_BATTERY_CHECK_TIME)) { + frontend_writeStatusIcons(appData); + appData->iDisplay.show(); + }; + + sample_synchronize(appData, diffTick); + + xtotp_processStateMachine(appData, inputState, buttonPos); + appData->secFlag = false; + } + + return EXIT_SUCCESS; +} + +void sample_synchronize(xtotp_Data *appData, uint32_t diffTick) +{ + static uint32_t syncErrorTime = 0; + if (appData->iSync.isSynchronizing) { + if (batMeas_getBatteryPercentState(&appData->iBatMeas) < + XTOTP_SYNC_MIN_BATTERY_LEVEL) { + sync_abort(&appData->iSync); + syncErrorTime = 0; + appData->iSync.syncDuration = UINT32_MAX; + frontend_writeStatusIcons(appData); + appData->processData.updateDisplayData = true; + return; + } + appData->iSync.syncDuration += diffTick; + if (syncErrorTime > XTOTP_SYNC_ERROR_RESET) { + appData->iSync.syncDuration = 0; + appData->iSync.powerOn(); + syncErrorTime = 0; + } + timeoutTick = 0; // Do not power off during synchronizing + tinyTimeType syncTime; + switch (appData->iSync.synchronize(&syncTime)) { + case SYNC_WAITING: + LOGGER_TRACE("Waiting for sync data"); + syncErrorTime = 0; + break; + case SYNC_TIME_ONLY: + LOGGER_DEBUG("Received only time: %.2d:%.2d:%.2d", syncTime.hour, + syncTime.min, syncTime.sec); + syncErrorTime = 0; + /* To define what to do */ + break; + case SYNC_TIME_AND_DATE: + // Reset time in clock peripheral + appData->iClock.powerOn(); + // Calculate new received unix time + uint64_t syncUnixTime = (uint64_t)tiny_getUnixTime(&syncTime); + LOGGER_DEBUG("Received time and date: %.2u:%.2u:%.2u %.2u.%.2u.%.4u", + syncTime.hour, syncTime.min, syncTime.sec, syncTime.monthDay, + syncTime.month, syncTime.year); + // Get difference of current time + uint64_t oldClockTime = appData->iClock.getUnixTime(); + appData->iClock.setUnixTime(syncUnixTime); + appData->iClock.powerOff(); + if (syncUnixTime > oldClockTime) { + appData->iSync.syncDiff = syncUnixTime - oldClockTime; + } else { + appData->iSync.syncDiff = oldClockTime - syncUnixTime; + } + appData->iSync.lastSyncTime = syncUnixTime; + appData->iSync.expectedSyncTime = syncUnixTime + XTOTP_SYNC_UPDATE_PERIOD; + sync_abort(&appData->iSync); + syncErrorTime = 0; + // Update frontend + frontend_writeStatusIcons(appData); + appData->processData.updateDisplayData = true; + appData->iDisplay.show(); + break; + case SYNC_ERROR: + default: + syncErrorTime += diffTick; + if (syncErrorTime > XTOTP_SYNC_ERROR_RESET) { + LOGGER_INFO("SYNC failed %u => reset", syncErrorTime); + appData->iSync.powerOff(); + } + break; + } + } +} +\ No newline at end of file diff --git a/xotp_app/app/src/xtotpCryptoHandler.c b/xotp_app/app/src/xtotpCryptoHandler.c @@ -0,0 +1,168 @@ +/** + * @file xtotpCryptoHandler.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Handler the (x)TOTP crypto data and generates the current codes + * @version 0.1 + * @date 03-08-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#include "xtotpCryptoHandler.h" + +#include <stdlib.h> +#include <string.h> + +#include "base32_converter.h" +#include "base8_converter.h" + +/** + * @brief Saves the algorithm names + * To save storage, the algorithms for base TOTP are not stored. + * THey use the same index as for the xTOTP and begin the string without the x + * + */ +const char *algNames[] = { + [ALG_NONE] = "Not defined", [ALG_TOTP_SHA1] = "xTOTP-SHA1"}; + +crypto_algoInfo crypto_getAlgoInfo(xtotpAlgoSettingsType *algoSettings) +{ + if (NULL == algoSettings || ALG_NONE == algoSettings->algorithm) { + return ALGO_UNINITIALIZED; + } + if (algoSettings->algorithm > ALG_CHANGE_VALUE && + algoSettings->algorithm < ALG_MAX_XTOTP_ALGORITHM) { + return ALGO_XTOTP; + } else if (algoSettings->algorithm > ALG_NONE && + algoSettings->algorithm < ALG_MAX_TOTP_ALGORITHM) { + return ALGO_TOTP; + } else { + return ALGO_UNINITIALIZED; + } +} + +const char *crypto_getAlgoName(xtotpAlgoSettingsType *algoSettings) +{ + if (NULL == algoSettings) { + return NULL; + } + switch (crypto_getAlgoInfo(algoSettings)) { + case ALGO_UNINITIALIZED: + break; + case ALGO_TOTP: + if (algoSettings->algorithm < XTOTP_ARRAY_SIZE(algNames)) { + return (algNames[algoSettings->algorithm] + 1); + } + break; + case ALGO_XTOTP: + if ((algoSettings->algorithm & 0x0F) < XTOTP_ARRAY_SIZE(algNames)) { + return algNames[(algoSettings->algorithm & 0x0F)]; + } + break; + default: + break; + } + return algNames[ALG_NONE]; +} + +crypto_algoInfo crypto_initAlgoSettings(xtotpAlgoSettingsType *algoSettings, + const uint8_t *secret, + uint8_t secretLen, + crypto_secretBase secretBase, + uint8_t interval, + uint8_t passcodeDigits, + crypto_algorithms algorithm, + const uint8_t *currency, + uint32_t fraction, + const uint8_t *merchantTemplate) +{ + if (NULL == algoSettings || ALG_NONE == algorithm) { + return ALGO_UNINITIALIZED; + } + algoSettings->algorithm = ALG_NONE; + if (NULL == secret || 0 == secretLen) { + return ALGO_UNINITIALIZED; + } + switch (secretBase) { + case SECRET_BASE_BYTE: + if (TALER_SECRET_LEN < secretLen) { + return ALGO_UNINITIALIZED; + } else { + memcpy(algoSettings->secret, secret, secretLen); + algoSettings->secretLen = secretLen; + } + break; + case SECRET_BASE_8: + if (base8_decodeNum(algoSettings->secret, + (uint32_t *)&algoSettings->secretLen, TALER_SECRET_LEN, + secret, secretLen) != BASEX_OK) { + return ALGO_UNINITIALIZED; + } + break; + case SECRET_BASE_32: + if (base32_decodeString( + algoSettings->secret, (uint32_t *)&algoSettings->secretLen, + TALER_SECRET_LEN, (const char *)secret) != BASEX_OK) { + return ALGO_UNINITIALIZED; + } + break; + default: + return ALGO_UNINITIALIZED; + break; + } + algoSettings->digits = (passcodeDigits < MAX_PASSCODE_LENGTH_DIGITS) + ? passcodeDigits + : MAX_PASSCODE_LENGTH_DIGITS; + algoSettings->interval = interval; + + // Check if the currency must be copied + if (algorithm > ALG_CHANGE_VALUE && algorithm < ALG_MAX_XTOTP_ALGORITHM) { + if (currency) { + TALER_xDataInit(&algoSettings->xTalerData, currency, fraction, + merchantTemplate); + algoSettings->algorithm = algorithm; + return ALGO_XTOTP; + } else { + return ALGO_UNINITIALIZED; + } + } else if (algorithm < ALG_MAX_TOTP_ALGORITHM) { + algoSettings->algorithm = algorithm; + return ALGO_TOTP; + } else { + return ALGO_UNINITIALIZED; + } +} + +void crypto_setMerchantData(xtotpCryptoHandler *cryptoHandler, + const char *deviceID, + const char *merchantBackend) +{ + if (NULL == cryptoHandler) { + return; + } + if (deviceID) { + strncpy(cryptoHandler->deviceID, deviceID, XTOTP_DEVICE_ID_SIZE); + } + if (merchantBackend) { + strncpy(cryptoHandler->merchandBackend, merchantBackend, + XTOTP_MERCHANT_BACKEND_SIZE); + } +} + +xtotpAlgoSettingsType * +crypto_getCurrentAlgorithm(xtotpCryptoHandler *cryptoHandler) +{ + return &cryptoHandler->algorithms[cryptoHandler->usedAlgorithm]; +} +\ No newline at end of file diff --git a/xotp_app/app/src/xtotpRegisterSecret.c b/xotp_app/app/src/xtotpRegisterSecret.c @@ -0,0 +1,563 @@ +/** + * @file xtotpRegisterSecret.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Settings state machine to register new secret with a corresponding + * althorihm Changing and selecting is as well intergrated in this module + * @version 0.1 + * @date 24-07-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "xtotpRegisterSecret.h" +#include "base32_converter.h" +#include "base8_converter.h" +#include "frontend.h" +#include "xtotpStateMachine.h" +#include <string.h> + +static uint64_t usedInputNumber = 0; + +void registerDispatcherEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerDispatcher(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSelectEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSelect(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSetAlgorithmEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSetAlgorithm(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSetSecretEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSetSecret(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSetCodeLengthEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSetCodeLength(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSetIntervalEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSetInterval(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSetCurrencyEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSetCurrency(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void registerSetFractionEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void registerSetFraction(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +stateHandler registerSecretStates[XTOTP_REGISTER_SECRET_MAX_STATES] = { + [XTOTP_REGISTER_SECRET_DISPATCHER] = {.entryAction = + registerDispatcherEntry, + .runAction = registerDispatcher}, + [XTOTP_REGISTER_SECRET_SELECT] = {.entryAction = registerSelectEntry, + .runAction = registerSelect}, + [XTOTP_REGISTER_SECRET_SET_ALGORITHM] = {.entryAction = + registerSetAlgorithmEntry, + .runAction = registerSetAlgorithm}, + [XTOTP_REGISTER_SECRET_SET_SECRET] = {.entryAction = registerSetSecretEntry, + .runAction = registerSetSecret}, + [XTOTP_REGISTER_SECRET_SET_CODE_LENGTH] = {.entryAction = + registerSetCodeLengthEntry, + .runAction = + registerSetCodeLength}, + [XTOTP_REGISTER_SECRET_SET_INTERVAL] = {.entryAction = + registerSetIntervalEntry, + .runAction = registerSetInterval}, + [XTOTP_REGISTER_SECRET_SET_CURRENCY] = {.entryAction = + registerSetCurrencyEntry, + .runAction = registerSetCurrency}, + [XTOTP_REGISTER_SECRET_SET_FRACTION] = {.entryAction = + registerSetFractionEntry, + .runAction = registerSetFraction}}; + +void settings_processSecretHandling(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + xtotp_RegisterSecretStates oldState = appData->currentStates.registerSecret; + if (appData->currentStates.registerSecret >= + XTOTP_REGISTER_SECRET_MAX_STATES) { + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER; + } else { + registerSecretStates[appData->currentStates.registerSecret].runAction( + appData, inputState, inputButton); + } + if (oldState != appData->currentStates.registerSecret) { + registerSecretStates[appData->currentStates.registerSecret].entryAction( + appData, inputState, inputButton); + } +} + +void registerDispatcherEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + // Nothing TO DO + (void)appData; + (void)inputState; + (void)inputButton; +} + +void registerDispatcher(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + if (crypto_getAlgoInfo( + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm]) == + ALGO_UNINITIALIZED) { + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_SET_ALGORITHM; + } else { + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_SELECT; + } + return; +} + +void registerSelectEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Stored Secret", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "Selected: %3u", + appData->cryptoHandler.processedAlgorithm); + xtotpAlgoSettingsType *selectedAlg = + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm]; + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "%us %1uD %s", + selectedAlg->interval, selectedAlg->digits, + crypto_getAlgoName(selectedAlg)); + appData->iDisplay.show(); + usedInputNumber = 0; +} + +void registerSelect(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (INPUT_STATE_RISING_EDGE == inputState) { + switch (inputButton) { + case INPUT_ENTER: + appData->cryptoHandler.usedAlgorithm = + appData->cryptoHandler.processedAlgorithm; + appData->currentStates.base = XTOTP_DISPATCHER; + break; + case INPUT_DEL: + appData->currentStates.settings = SETTINGS_ENTRY; + break; + case INPUT_0: + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_ALGORITHM; + break; + default: + break; + } + } +} + +void registerSetAlgorithmEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Set Alg", NULL); + if (ALGO_UNINITIALIZED == + crypto_getAlgoInfo( + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm])) { + memset(&appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT], 0, + sizeof(xtotpAlgoSettingsType)); + } else { + memcpy(&appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT], + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm], + sizeof(xtotpAlgoSettingsType)); + } + usedInputNumber = + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm; + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_LINE1, "New alg: %3u", + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm); + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_LINE2, "%s <=", + crypto_getAlgoName( + &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT])); + appData->iDisplay.show(); +} +void registerSetAlgorithm(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + + if (INPUT_STATE_RISING_EDGE == inputState) { + if (input_readNumber(&usedInputNumber, ALG_MAX_XTOTP_ALGORITHM - 1, + inputButton)) { + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm = + (crypto_algorithms)usedInputNumber; + if (ALGO_UNINITIALIZED != + crypto_getAlgoInfo( + &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT])) { + // Algorithm OK + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_SECRET; + return; + } + } + if (INPUT_DEL == inputButton && 0 == usedInputNumber) { + // return to settings + appData->currentStates.settings = SETTINGS_ENTRY; + return; + } + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm = + (crypto_algorithms)usedInputNumber; + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_LINE1, "New alg: %3u", + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm); + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_LINE2, "%s <=", + crypto_getAlgoName( + &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT])); + appData->iDisplay.show(); + } +} + +void registerSetSecretEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + if (appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].secretLen != + 0) { + // Secret already stored + frontend_createBaseWindow(appData, "Secret Enter", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "Saved secret"); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "Enter to skip"); + } else { + frontend_createBaseWindow(appData, "Secret Enter", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, + "Enter new secret"); + } + input_readBase8(INPUT_NONE, NULL, NULL, 0, true); + appData->iDisplay.show(); +} + +void registerSetSecret(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + uint8_t *readInput = NULL; + uint8_t inputLength = 0; + if (XTOTP_REACTION_EDGE == inputState) { + if (input_readBase8(inputButton, &readInput, &inputLength, + TALER_SECRET_BASE8_LENGTH, false) || + INPUT_ENTER == inputButton) { + xtotpAlgoSettingsType *registerAlgo = + &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]; + uint32_t secretLength = 0; + if (inputLength > 0) { + uint8_t inputAsNum[TALER_SECRET_BASE8_LENGTH]; + if (BASEX_OK != + base8_stringToNum(inputAsNum, (const char *)readInput) || + BASEX_OK != base8_decodeNum(registerAlgo->secret, &secretLength, + TALER_CURRENCY_LEN, inputAsNum, + inputLength)) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + "SRC ERROR"); + appData->iDisplay.show(); + return; + } + } + if (inputLength > 0) { + registerAlgo->secretLen = (uint8_t)secretLength; + } + if (registerAlgo->secretLen > 0) { + // Secret accepted, go to next step + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_CODE_LENGTH; + return; + } + } + if (INPUT_DEL == inputButton && 0 == inputLength) { + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER; + return; + } + // Display current input + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + (char *)readInput); + appData->iDisplay.show(); + } +} + +void registerSetCodeLengthEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + const char *menuTitle = "Digits"; + xtotpAlgoSettingsType *registerAlgo = + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm]; + if (ALGO_UNINITIALIZED == crypto_getAlgoInfo(registerAlgo)) { + usedInputNumber = 0; + frontend_createBaseWindow(appData, menuTitle, "Set new: "); + } else { + usedInputNumber = registerAlgo->digits; + frontend_createBaseWindow(appData, menuTitle, NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %1d", + (uint32_t)usedInputNumber); + } + appData->iDisplay.show(); + return; +} +void registerSetCodeLength(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (XTOTP_REACTION_EDGE == inputState) { + if (input_readNumber(&usedInputNumber, TALER_MAX_PASSCODE_LENGTH, + inputButton)) { + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].digits = + (uint8_t)usedInputNumber; + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_INTERVAL; + return; + } + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %1d", + (uint32_t)usedInputNumber); + appData->iDisplay.show(); + } +} + +void registerSetIntervalEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + const char *menuTitle = "Interval"; + xtotpAlgoSettingsType *registerAlgo = + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm]; + if (ALGO_UNINITIALIZED == crypto_getAlgoInfo(registerAlgo)) { + usedInputNumber = 0; + frontend_createBaseWindow(appData, menuTitle, "Set new: "); + } else { + usedInputNumber = registerAlgo->interval; + frontend_createBaseWindow(appData, menuTitle, NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %3d", + (uint32_t)usedInputNumber); + } + appData->iDisplay.show(); + return; +} +void registerSetInterval(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (XTOTP_REACTION_EDGE == inputState) { + if (input_readNumber(&usedInputNumber, UINT8_MAX, inputButton)) { + xtotpAlgoSettingsType *newAlgo = + &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]; + newAlgo->interval = (uint8_t)usedInputNumber; + switch (crypto_getAlgoInfo(newAlgo)) { + case ALGO_TOTP: + // Registration finished + crypto_initAlgoSettings( + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm], + newAlgo->secret, newAlgo->secretLen, SECRET_BASE_BYTE, + newAlgo->interval, newAlgo->digits, newAlgo->algorithm, NULL, 0, + NULL); + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_DISPATCHER; + return; + case ALGO_XTOTP: + // Go to register currency + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_CURRENCY; + return; + default: + // An error occurs + appData->currentStates.settings = SETTINGS_ENTRY; + return; + } + } + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %3d", + (uint32_t)usedInputNumber); + appData->iDisplay.show(); + } +} + +void registerSetCurrencyEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Currency", NULL); + if (strlen((const char *)appData->cryptoHandler + .algorithms[CRYPTO_NEW_SECRET_SEGMENT] + .xTalerData.currency) == 0) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "B8:%16s", " "); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "=>:%16s", " "); + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "Registered:"); + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_LINE2, "%s", + appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT] + .xTalerData.currency); + } + input_readBase8(INPUT_NONE, NULL, NULL, 0, true); + appData->iDisplay.show(); +} +void registerSetCurrency(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ +#define CURRENCY_LENGTH_AS_BASE8 \ + ((TALER_CURRENCY_LEN * 8 + 2) / BASE8_BIT_LENGTH) + + if (XTOTP_REACTION_EDGE == inputState) { + uint8_t *inputString; + uint8_t inputLength = 0; + bool readState = input_readBase8(inputButton, &inputString, &inputLength, + CURRENCY_LENGTH_AS_BASE8, false); + if (INPUT_ENTER == inputButton && + 0 != strlen((const char *)appData->cryptoHandler + .algorithms[CRYPTO_NEW_SECRET_SEGMENT] + .xTalerData.currency) && + 0 == inputLength) { + // Take current stored string + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_FRACTION; + return; + } + inputString[inputLength] = '\0'; + uint8_t inputAsNum[CURRENCY_LENGTH_AS_BASE8]; + uint8_t newCurrency[TALER_CURRENCY_LEN] = ""; + uint32_t currencyLength = 0; + baseX_returnType retState = BASEX_OK; + if (BASEX_OK == + (retState = base8_stringToNum(inputAsNum, (const char *)inputString))) { + retState = base8_decodeNum(newCurrency, &currencyLength, + CURRENCY_LENGTH_AS_BASE8, inputAsNum, + (const uint32_t)inputLength); + if (BASEX_OK == retState && readState) { + newCurrency[currencyLength] = '\0'; + memcpy(appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT] + .xTalerData.currency, + newCurrency, currencyLength + 1); + appData->currentStates.registerSecret = + XTOTP_REGISTER_SECRET_SET_FRACTION; + return; + } + } + if (retState != BASEX_OK) { + // Error, copy error msg + strncpy((char *)newCurrency, "SRC ERROR", TALER_CURRENCY_LEN); + } + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "B8:%16s", + inputString); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "=>:%16s", + newCurrency); + appData->iDisplay.show(); + } +} + +void registerSetFractionEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + xtotpAlgoSettingsType *registerAlgo = + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm]; + frontend_createBaseWindow(appData, "Fraction", NULL); + if (ALGO_UNINITIALIZED == crypto_getAlgoInfo(registerAlgo)) { + usedInputNumber = 0; + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set:%*s", 6, + " "); + } else { + usedInputNumber = registerAlgo->xTalerData.fraction; + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set:%*d", 6, + (uint32_t)usedInputNumber); + } + appData->iDisplay.show(); +} +void registerSetFraction(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (XTOTP_REACTION_EDGE == inputState) { + if (input_readNumber(&usedInputNumber, TALER_AMOUNT_FRAC_BASE, + inputButton) || + // 0 Also allowed for no fraction + (0 == usedInputNumber && INPUT_ENTER == inputButton)) { + xtotpAlgoSettingsType *newAlgo = + &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]; + newAlgo->xTalerData.fraction = (uint32_t)usedInputNumber; + // Finish registration + crypto_initAlgoSettings( + &appData->cryptoHandler + .algorithms[appData->cryptoHandler.processedAlgorithm], + newAlgo->secret, newAlgo->secretLen, SECRET_BASE_BYTE, + newAlgo->interval, newAlgo->digits, newAlgo->algorithm, + newAlgo->xTalerData.currency, newAlgo->xTalerData.fraction, NULL); + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER; + return; + } + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set:%*d", 6, + (uint32_t)usedInputNumber); + appData->iDisplay.show(); + } +} +\ No newline at end of file diff --git a/xotp_app/app/src/xtotpSettings.c b/xotp_app/app/src/xtotpSettings.c @@ -0,0 +1,550 @@ +/** + * @file xtotpSettings.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Settings state machine for secret registration and additional settings + * @version 0.1 + * @date 27-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "xtotpSettings.h" + +// #include "version.h" + +#include <stdio.h> +#include <string.h> + +#include "base32_converter.h" +#include "base8_converter.h" + +#include "frontend.h" +#include "inputInterface.h" +#include "logger.h" +#include "tinytime.h" +#include "xtotpConfig.h" +#include "xtotpStateMachine.h" +#include "xtotpUtil.h" + +/** + * @brief This function sets for the additional settings the next state with the + * given input. + * + * This function needs to be called if no special settings is enabled (like + * updating a value). + * @note To be free to use this function, call it in the state as you like + * (rising/falling edge/ high). + * + * @param appData The user data handler + * @param inputButton The current input button + */ +void checkBaseAdditionalInput(xtotp_Data *appData, inputButtonPos inputButton); + +/* Additional state callback functions */ +void additionalClockEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalClock(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalBatteryStateEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalBatteryState(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalLastSyncTimeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalLastSyncTime(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalDifferenceSyncEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalDifferenceSync(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalTimeoutTimeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalTimeoutTime(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalScreenModeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalScreenMode(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalDeviceIDEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalDeviceID(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalFirmwareEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalFirmware(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void additionalUnusedEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void additionalUnused(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +stateHandler additionalSettingsState[ADDITIONAL_MAX_STATES] = { + [ADDITIONAL_CLOCK] = {.entryAction = additionalClockEntry, + .runAction = additionalClock}, + [ADDITIONAL_BATTERY_STATE] = {.entryAction = additionalBatteryStateEntry, + .runAction = additionalBatteryState}, + [ADDITIONAL_LAST_SYNC_TIME] = {.entryAction = additionalLastSyncTimeEntry, + .runAction = additionalLastSyncTime}, + [ADDITIONAL_DIFFERENCE_SYNC] = {.entryAction = + additionalDifferenceSyncEntry, + .runAction = additionalDifferenceSync}, + [ADDITIONAL_TIMEOUT_TIME] = {.entryAction = additionalTimeoutTimeEntry, + .runAction = additionalTimeoutTime}, + [ADDITIONAL_SCREEN_MODE] = {.entryAction = additionalScreenModeEntry, + .runAction = additionalScreenMode}, + [ADDITIONAL_DEVICE_ID] = {.entryAction = additionalDeviceIDEntry, + .runAction = additionalDeviceID}, + [ADDITIONAL_UNUSED_7] = {.entryAction = additionalUnusedEntry, + .runAction = additionalUnused}, + [ADDITIONAL_UNSUED_8] = {.entryAction = additionalUnusedEntry, + .runAction = additionalUnused}, + [ADDITIONAL_FIRMWARE] = {.entryAction = additionalFirmwareEntry, + .runAction = additionalFirmware}}; + +void settings_processAdditional(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (NULL == appData) { + return; + } + xtotp_AdditionalSettingStates oldState = + appData->currentStates.additionalSettings; + if (appData->currentStates.additionalSettings >= ADDITIONAL_MAX_STATES) { + appData->currentStates.additionalSettings = ADDITIONAL_CLOCK; + } else { + additionalSettingsState[appData->currentStates.additionalSettings] + .runAction(appData, inputState, inputButton); + } + if (oldState != appData->currentStates.additionalSettings) { + additionalSettingsState[appData->currentStates.additionalSettings] + .entryAction(appData, inputState, inputButton); + } +} + +void checkBaseAdditionalInput(xtotp_Data *appData, inputButtonPos inputButton) +{ + switch (inputButton) { + case INPUT_0: + appData->currentStates.additionalSettings = ADDITIONAL_CLOCK; + return; + case INPUT_1: + appData->currentStates.additionalSettings = ADDITIONAL_BATTERY_STATE; + return; + case INPUT_2: + appData->currentStates.additionalSettings = ADDITIONAL_LAST_SYNC_TIME; + return; + case INPUT_3: + appData->currentStates.additionalSettings = ADDITIONAL_DIFFERENCE_SYNC; + return; + case INPUT_4: + appData->currentStates.additionalSettings = ADDITIONAL_TIMEOUT_TIME; + return; + case INPUT_5: + appData->currentStates.additionalSettings = ADDITIONAL_SCREEN_MODE; + return; + case INPUT_6: + appData->currentStates.additionalSettings = ADDITIONAL_DEVICE_ID; + return; + case INPUT_9: + appData->currentStates.additionalSettings = ADDITIONAL_FIRMWARE; + return; + case INPUT_ENTER: + appData->currentStates.base = XTOTP_DISPATCHER; + return; + case INPUT_DEL: + appData->currentStates.base = XTOTP_DISPATCHER; + return; + default: + // Do nothing + return; + } +} + +void additionalClockEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + appData->processData.lastNeededTime = 0; + frontend_createBaseWindow(appData, "Time & Date", NULL); + additionalClock(appData, INPUT_STATE_LOW, INPUT_NONE); + return; +} + +void additionalClock(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (appData->secFlag || 0 == appData->processData.lastNeededTime) { + appData->iClock.powerOn(); + uint64_t currentTime = appData->iClock.getUnixTime(); + appData->iClock.powerOff(); + if (currentTime != appData->processData.lastNeededTime) { + appData->processData.lastNeededTime = currentTime; + tinyTimeType tad; + tiny_getTimeType(&tad, appData->processData.lastNeededTime); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, + "%.2u:%.2u:%.2u", tad.hour, tad.min, tad.sec); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "%.2u.%.2u.%.4u", tad.monthDay, tad.month, + tad.year); + appData->iDisplay.show(); + } + } + if (inputState == INPUT_STATE_RISING_EDGE) { + checkBaseAdditionalInput(appData, inputButton); + } +} + +void additionalBatteryStateEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + batMeas_measure(&appData->iBatMeas, appData->iDevice.getTick(), APP_SEC_BASE); + frontend_createBaseWindow(appData, "Battery", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%u mV", + batMeas_getBatteryVoltageState(&appData->iBatMeas)); + appData->iDisplay.show(); + return; +} +void additionalBatteryState(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (!batMeas_measure(&appData->iBatMeas, appData->iDevice.getTick(), + APP_SEC_BASE)) { + frontend_writeStatusIcons(appData); + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_CENTRED, "%u mV", + batMeas_getBatteryVoltageState(&appData->iBatMeas)); + appData->iDisplay.show(); + } + if (inputState == INPUT_STATE_RISING_EDGE) { + checkBaseAdditionalInput(appData, inputButton); + } +} + +void additionalLastSyncTimeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Last Sync", NULL); + appData->processData.updateDisplayData = true; + additionalLastSyncTime(appData, INPUT_STATE_LOW, INPUT_NONE); +} + +void additionalLastSyncTime(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (appData->processData.updateDisplayData) { + tinyTimeType tad; + tiny_getTimeType(&tad, appData->iSync.lastSyncTime); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, + "%.2u:%.2u:%.2u %.2u.%.2u.%.4u", tad.hour, tad.min, + tad.sec, tad.monthDay, tad.month, tad.year); + tiny_getTimeType(&tad, appData->iSync.expectedSyncTime); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "%.2u:%.2u:%.2u %.2u.%.2u.%.4u", tad.hour, tad.min, + tad.sec, tad.monthDay, tad.month, tad.year); + appData->iDisplay.show(); + } + // Check window update + if (INPUT_STATE_RISING_EDGE == inputState) { + if (INPUT_DEL == inputButton && !appData->iSync.isSynchronizing) { + sync_enable(&appData->iSync); + frontend_writeStatusIcons(appData); + appData->iDisplay.show(); + return; + } + checkBaseAdditionalInput(appData, inputButton); + } +} + +void additionalDifferenceSyncEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Sync Info", NULL); + appData->processData.updateDisplayData = true; + additionalDifferenceSync(appData, INPUT_STATE_LOW, inputButton); + return; +} +void additionalDifferenceSync(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + // Update at startup/ new data + if (appData->processData.updateDisplayData) { + frontend_writeStatusIcons(appData); + uint64_t syncHour = 0; + uint64_t syncMin = 0; + uint64_t syncSec = tiny_convertSeconds( + MS_TO_SECOND(appData->iSync.syncDiff), NULL, &syncHour, &syncMin); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, + "Dif:%7s%.2u:%.2u:%.2u", " ", (uint32_t)syncHour, + (uint32_t)syncMin, (uint32_t)syncSec); + if (appData->iSync.syncDuration >= XTOTP_SYNC_MAX_SYNC_TIME) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "Dur: Sync error"); + } else { + syncSec = tiny_convertSeconds(MS_TO_SECOND(appData->iSync.syncDuration), + NULL, &syncHour, &syncMin); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "Dur:%7s%.2u:%.2u:%.2u", " ", (uint32_t)syncHour, + (uint32_t)syncMin, (uint32_t)syncSec); + } + appData->iDisplay.show(); + } + // Sync time + else if (appData->iSync.isSynchronizing && appData->secFlag) { + uint64_t syncHour = 0; + uint64_t syncMin = 0; + uint64_t syncSec = tiny_convertSeconds( + MS_TO_SECOND(appData->iSync.syncDuration), NULL, &syncHour, &syncMin); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "Dur:%7s%.2u:%.2u:%.2u", " ", (uint32_t)syncHour, + (uint32_t)syncMin, (uint32_t)syncSec); + appData->iDisplay.show(); + } + // Check window update + if (INPUT_STATE_RISING_EDGE == inputState) { + if (INPUT_DEL == inputButton && !appData->iSync.isSynchronizing) { + sync_enable(&appData->iSync); + frontend_writeStatusIcons(appData); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "Dur:%7s%.2u:%.2u:%.2u", " ", 0, 0, 0); + appData->iDisplay.show(); + return; + } + checkBaseAdditionalInput(appData, inputButton); + } +} + +void additionalTimeoutTimeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Sleep Timeout", NULL); + if (appData->timeOutTime != 0) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%3u s", + MS_TO_SECOND(appData->timeOutTime)); + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Disabled"); + } + appData->iDisplay.show(); + return; +} +bool setNewTime = false; +uint64_t newTimeOut = 0; + +void additionalTimeoutTime(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + + if (inputState == XTOTP_REACTION_EDGE) { + if (INPUT_DEL == inputButton && !setNewTime) { + newTimeOut = 0; + setNewTime = true; + } + if (setNewTime) { + if (input_readNumber(&newTimeOut, XTOTP_MAX_TIMEOUT_TIME, inputButton) || + (0 == newTimeOut && INPUT_ENTER == inputButton)) { + appData->timeOutTime = (uint32_t)SECOND_TO_MS(newTimeOut); + if (0 != newTimeOut) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + " %3u s", + MS_TO_SECOND(appData->timeOutTime)); + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + "Disabled"); + } + appData->iDisplay.show(); + setNewTime = false; + newTimeOut = 0; + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + "New: %3u s", (uint32_t)newTimeOut); + appData->iDisplay.show(); + } + } else { + checkBaseAdditionalInput(appData, inputButton); + } + } +} + +void additionalScreenModeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Theme", + (appData->iDisplay.theme == PBM_BLACK) ? "Light" + : "Dark"); + appData->iDisplay.show(); + return; +} +void additionalScreenMode(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (inputState == INPUT_STATE_RISING_EDGE) { + if (inputButton == INPUT_DEL) { + appData->iDisplay.theme = + (appData->iDisplay.theme == PBM_BLACK) ? PBM_WHITE : PBM_BLACK; + additionalScreenModeEntry(appData, inputState, inputButton); + } else { + checkBaseAdditionalInput(appData, inputButton); + } + } +} + +static bool setNewDeviceID = false; + +void additionalDeviceIDEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)appData; + (void)inputState; + setNewDeviceID = false; + frontend_createBaseWindow(appData, + "Device ID:", appData->cryptoHandler.deviceID); + input_readBase8(inputButton, NULL, NULL, (XTOTP_DEVICE_ID_SIZE - 1), true); + appData->iDisplay.show(); + return; +} + +void additionalDeviceID(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (INPUT_STATE_RISING_EDGE == inputState) { + if (INPUT_DEL == inputButton && !setNewDeviceID) { + setNewDeviceID = true; + } + if (setNewDeviceID) { + uint8_t *inputString = NULL; + uint8_t inputLength = 0; + if (input_readBase8(inputButton, &inputString, &inputLength, + ((XTOTP_DEVICE_ID_SIZE * 8 + 2) / 3), false)) { + LOGGER_DEBUG("Got input string: %s with length: %u", inputString, + inputLength); + uint8_t numBuf[(XTOTP_DEVICE_ID_SIZE * 8 / 3)]; + base8_stringToNum(numBuf, (const char *)inputString); + uint32_t decodedLength = 0; + if (base8_decodeNum((uint8_t *)appData->cryptoHandler.deviceID, + &decodedLength, (XTOTP_DEVICE_ID_SIZE - 1), numBuf, + inputLength) == BASEX_OK) { + appData->cryptoHandler.deviceID[decodedLength] = '\0'; + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + appData->cryptoHandler.deviceID); + input_readBase8(inputButton, NULL, NULL, (XTOTP_DEVICE_ID_SIZE - 1), + true); + setNewDeviceID = false; + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + "ERR: PARSE"); + } + appData->iDisplay.show(); + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + (const char *)inputString); + appData->iDisplay.show(); + } + } else { + checkBaseAdditionalInput(appData, inputButton); + } + } +} + +void additionalFirmwareEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Firmware", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "VER: %14s", + FIRMWARE_VERSION); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "DAT: %14s", + FIRMWARE_DATE); + appData->iDisplay.show(); +} + +void additionalFirmware(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (inputState == INPUT_STATE_RISING_EDGE) { + checkBaseAdditionalInput(appData, inputButton); + } +} + +void additionalUnusedEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + appData->currentStates.additionalSettings = ADDITIONAL_CLOCK; +} +void additionalUnused(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + appData->currentStates.additionalSettings = ADDITIONAL_CLOCK; +} +\ No newline at end of file diff --git a/xotp_app/app/src/xtotpStateMachine.c b/xotp_app/app/src/xtotpStateMachine.c @@ -0,0 +1,726 @@ +/** + * @file xtotpStateMachine.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief State machine of the xtotp device + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "xtotpStateMachine.h" +#include "logger.h" + +#include <stdio.h> +#include <string.h> + +#include "frontend.h" +#include "talerAmount.h" +#include "xtotpConfig.h" +#include "xtotpCryptoHandler.h" +#include "xtotpRegisterSecret.h" +#include "xtotpSettings.h" +#include "xtotpUtil.h" + +#define ARROW_RIGHT_SYMBOL (26) ///< Symbol for right arrow +#define ARROW_LEFT_SYMBOL (27) ///< Symbol for left arrow + +void sleepEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void sleep(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void startupEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void startup(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void dispatcherEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void dispatcher(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void enterAmountEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void enterAmount(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void autoPayStartEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void autoPayStart(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void autoPayWaitResposeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void autoPayWaitRespose(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void autoPayVerifyEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void autoPayVerify(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void showXTOTPEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void showXTOTP(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void showTOTPEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void showTOTP(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void goToSettingsEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void goToSettings(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +void settingsEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); +void settings(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton); + +stateHandler stateCallbacks[XTOTP_MAX_STATES] = { + [XTOTP_SLEEP] = {.entryAction = sleepEntry, .runAction = sleep}, + [XTOTP_STARTUP] = {.entryAction = startupEntry, .runAction = startup}, + [XTOTP_DISPATCHER] = {.entryAction = dispatcherEntry, + .runAction = dispatcher}, + [XTOTP_ENTER_AMOUNT] = {.entryAction = enterAmountEntry, + .runAction = enterAmount}, + [XTOTP_AUTOPAY_START] = {.entryAction = autoPayStartEntry, + .runAction = autoPayStart}, + [XTOTP_AUTOPAY_WAIT_RESPONSE] = {.entryAction = autoPayWaitResposeEntry, + .runAction = autoPayWaitRespose}, + [XTOTP_AUTOPAY_VERIFY] = {.entryAction = autoPayVerifyEntry, + .runAction = autoPayVerify}, + [XTOTP_SHOW_XTOTP] = {.entryAction = showXTOTPEntry, + .runAction = showXTOTP}, + [XTOTP_SHOW_TOTP] = {.entryAction = showTOTPEntry, .runAction = showTOTP}, + [XTOTP_GO_TO_SETTINGS] = {.entryAction = goToSettingsEntry, + .runAction = goToSettings}, + [XTOTP_SETTINGS] = {.entryAction = settingsEntry, .runAction = settings}, +}; + +void xtotp_processStateMachine(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + xtotp_BaseStates entryState = appData->currentStates.base; + stateCallbacks[entryState].runAction(appData, inputState, inputButton); + while (entryState != appData->currentStates.base) { + entryState = appData->currentStates.base; + stateCallbacks[entryState].entryAction(appData, inputState, inputButton); + } + appData->processData.updateDisplayData = false; +} + +void sleepEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_clear(appData); + appData->iDisplay.show(); + return; // Do nothing +} + +void sleep(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + + appData->iDisplay.powerOff(); + appData->iInput.powerOff(); + appData->iClock.powerOff(); + appData->iSync.powerOff(); + appData->iBatMeas.powerOff(); + /* Shutdown device */ + appData->iDevice.shutdown(); + appData->currentStates.base = XTOTP_STARTUP; +} + +void startupEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + appData->iDisplay.powerOn(); + appData->iInput.powerOn(); + return; +} +void startup(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + // Unused parameters + (void)inputState; + (void)inputButton; + // Turn on display and input + appData->iDisplay.powerOn(); + appData->iInput.powerOn(); + appData->currentStates.base = XTOTP_DISPATCHER; + return; +} + +void dispatcherEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + xtotpAlgoSettingsType *usedSettings = + crypto_getCurrentAlgorithm(&appData->cryptoHandler); + switch (crypto_getAlgoInfo(usedSettings)) { + case ALGO_XTOTP: + appData->currentStates.base = XTOTP_ENTER_AMOUNT; + return; + case ALGO_TOTP: + appData->currentStates.base = XTOTP_SHOW_TOTP; + return; + case ALGO_UNINITIALIZED: + default: + break; + } + frontend_createBaseWindow(appData, "KEY SELECT", "NO SECRET"); + return; +} + +void dispatcher(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputButton; + if (INPUT_STATE_RISING_EDGE == inputState) { + appData->currentStates.base = XTOTP_SETTINGS; + } + return; +} + +void enterAmountEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + // Set amount to 0 + appData->processData.enteredAmount = 0; + frontend_createBaseWindow(appData, "Enter amount", NULL); + xtotpAlgoSettingsType *usedSettings = + crypto_getCurrentAlgorithm(&appData->cryptoHandler); + if (ALGO_XTOTP == crypto_getAlgoInfo(usedSettings)) { + if (usedSettings->xTalerData.fraction > 0) { + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_CENTRED, "%*u.%0*u", + MAX_AMOUNT_DIGITS - base10Exponent(usedSettings->xTalerData.fraction), + (uint32_t)appData->processData.enteredAmount / + usedSettings->xTalerData.fraction, + base10Exponent(usedSettings->xTalerData.fraction), + (uint32_t)appData->processData.enteredAmount % + usedSettings->xTalerData.fraction); + } else { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%*u", + MAX_AMOUNT_DIGITS, + (uint32_t)appData->processData.enteredAmount); + } + appData->iDisplay.show(); + } else { + appData->currentStates.base = XTOTP_SHOW_XTOTP; + } + return; +} + +void enterAmount(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (INPUT_STATE_RISING_EDGE == inputState) { + if (inputButton >= INPUT_NONE) { + return; + } + if (0 == appData->processData.enteredAmount && INPUT_0 == inputButton) { + appData->currentStates.base = XTOTP_GO_TO_SETTINGS; + return; + } + uint64_t oldAmount = appData->processData.enteredAmount; + if (input_readNumber(&appData->processData.enteredAmount, MAX_INPUT_LENGTH, + inputButton)) { + appData->currentStates.base = XTOTP_AUTOPAY_START; + return; + } + if (oldAmount != appData->processData.enteredAmount) { + xtotpAlgoSettingsType *usedSettings = + crypto_getCurrentAlgorithm(&appData->cryptoHandler); + + if (0 == usedSettings->xTalerData.fraction) { + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%*u", + MAX_AMOUNT_DIGITS, + (uint32_t)appData->processData.enteredAmount); + } else { + uint8_t amountValueOffset = + (uint8_t)MAX_AMOUNT_DIGITS - + base10Exponent(usedSettings->xTalerData.fraction); + uint32_t amountValue = (uint32_t)appData->processData.enteredAmount / + usedSettings->xTalerData.fraction; + uint8_t fractionLength = + base10Exponent(usedSettings->xTalerData.fraction); + uint32_t fraction = (uint32_t)appData->processData.enteredAmount % + usedSettings->xTalerData.fraction; + LOGGER_DEBUG("Current amount: %*u.%0*u", amountValueOffset, amountValue, + fractionLength, fraction); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, + "%*u.%0*u", amountValueOffset, amountValue, + fractionLength, fraction); + } + appData->iDisplay.show(); + } + } + return; +} + +void autoPayStartEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + xtotpAlgoSettingsType *usedAlg = + crypto_getCurrentAlgorithm(&appData->cryptoHandler); + if (strlen((const char *)usedAlg->xTalerData.merchantTemplateName) == 0) { + // No merchant template set, do not use autopay + appData->currentStates.base = XTOTP_SHOW_XTOTP; + return; + } + frontend_createBaseWindow(appData, "Autopay", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "ID:%16s", + appData->cryptoHandler.deviceID); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "T:%17s", + usedAlg->xTalerData.merchantTemplateName); + appData->iDisplay.show(); + // Send informations: + appData->iAutoPay.powerOn(); + if (usedAlg->xTalerData.fraction > 0) { + appData->iAutoPay.transmitState = appData->iAutoPay.sendURL( + "%s/%s?amount=%s:%u.%0*u", appData->cryptoHandler.merchandBackend, + usedAlg->xTalerData.merchantTemplateName, usedAlg->xTalerData.currency, + (uint32_t)appData->processData.enteredAmount / + usedAlg->xTalerData.fraction, + base10Exponent(usedAlg->xTalerData.fraction), + appData->processData.enteredAmount % usedAlg->xTalerData.fraction); + } else { + appData->iAutoPay.transmitState = appData->iAutoPay.sendURL( + "%s/%s?amount=%s:%u", appData->cryptoHandler.merchandBackend, + usedAlg->xTalerData.merchantTemplateName, usedAlg->xTalerData.currency, + (uint32_t)appData->processData.enteredAmount); + } + if (EXIT_SUCCESS == appData->iAutoPay.transmitState) { + appData->iAutoPay.powerOff(); + } + return; +} +void autoPayStart(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (EXIT_SUCCESS != appData->iAutoPay.transmitState) { + autoPayStartEntry(appData, inputState, inputButton); + } + if (XTOTP_REACTION_EDGE == inputState && INPUT_ENTER == inputButton) { + appData->currentStates.base = XTOTP_SHOW_XTOTP; + return; + } + if (appData->iAutoPay.isURLRead()) { + appData->currentStates.base = XTOTP_AUTOPAY_WAIT_RESPONSE; + return; + } + return; +} + +void autoPayWaitResposeEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Autopay", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, + "Wait for response"); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "Enter to skip"); + appData->iDisplay.show(); +} +void autoPayWaitRespose(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (XTOTP_REACTION_EDGE == inputState) { + if (INPUT_ENTER == inputButton) { + appData->currentStates.base = XTOTP_SHOW_XTOTP; + return; + } else if (INPUT_DEL == inputButton) { + appData->currentStates.base = XTOTP_AUTOPAY_START; + return; + } + } + if (appData->iAutoPay.arePasscodesReceived()) { + appData->currentStates.base = XTOTP_AUTOPAY_VERIFY; + } + return; +} + +void autoPayVerifyEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + uint32_t receivedPasscodes[XTOTP_AUTOPAY_RECEIVED_PASSKEYS]; + if (EXIT_FAILURE == appData->iAutoPay.getPasscodes(receivedPasscodes)) { + appData->currentStates.base = XTOTP_AUTOPAY_WAIT_RESPONSE; + return; + } + appData->iClock.powerOn(); + uint64_t currentTime = appData->iClock.getUnixTime(); + appData->iClock.powerOff(); + if (0 == currentTime) { + LOGGER_ERROR("Could not get the current time"); + return; + } + appData->processData.lastCode = + crypto_calc_OTP(&appData->cryptoHandler, &appData->iCrypto, currentTime, + appData->processData.enteredAmount); + + // Check passcodes: + bool verified = false; + for (uint8_t i = 0; i < XTOTP_AUTOPAY_RECEIVED_PASSKEYS; i++) { + if (appData->processData.lastCode == receivedPasscodes[i]) { + verified = true; + break; + } + } + + if (verified) { + frontend_createBaseWindow(appData, "Autopay", "Verified"); + } else { + frontend_createBaseWindow(appData, "Autopay", "Invalid!"); + } + appData->iDisplay.show(); + return; +} +void autoPayVerify(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (XTOTP_REACTION_EDGE == inputState) { + if (INPUT_ENTER == inputButton) { + appData->currentStates.base = XTOTP_ENTER_AMOUNT; + return; + } else if (INPUT_DEL == inputButton) { + appData->currentStates.base = XTOTP_AUTOPAY_WAIT_RESPONSE; + return; + } + } + return; +} + +void showXTOTPEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + if (ALGO_XTOTP != + crypto_getAlgoInfo(crypto_getCurrentAlgorithm(&appData->cryptoHandler))) { + appData->currentStates.base = XTOTP_DISPATCHER; + return; + } + frontend_createBaseWindow(appData, "xTOTP", NULL); + showXTOTP(appData, INPUT_STATE_RISING_EDGE, + INPUT_DEL); // Process to force an update action + return; +} + +void showXTOTP(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (INPUT_STATE_RISING_EDGE == inputState) { + switch (inputButton) { + case INPUT_DEL: + appData->iClock.powerOn(); + uint64_t currentTime = appData->iClock.getUnixTime(); + appData->iClock.powerOff(); + if (0 == currentTime) { + LOGGER_ERROR("Could not get the current time"); + return; + } + + // Get xTOTP code + appData->processData.lastCode = + crypto_calc_OTP(&appData->cryptoHandler, &appData->iCrypto, + currentTime, appData->processData.enteredAmount); + xtotpAlgoSettingsType *usedAlg = + crypto_getCurrentAlgorithm(&appData->cryptoHandler); + uint8_t halfCodeLength = usedAlg->digits / 2; + uint32_t decimalSeparator = power10(halfCodeLength); + LOGGER_INFO( + "Code generated %.*u %.*u", halfCodeLength, + (appData->processData.lastCode / decimalSeparator) % decimalSeparator, + halfCodeLength, appData->processData.lastCode % decimalSeparator); + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_CENTRED, "%.*u %.*u", halfCodeLength, + (appData->processData.lastCode / decimalSeparator) % decimalSeparator, + halfCodeLength, appData->processData.lastCode % decimalSeparator); + appData->iDisplay.show(); + break; + case INPUT_ENTER: + appData->currentStates.base = XTOTP_ENTER_AMOUNT; + return; + default: + return; + } + } +} + +void showTOTPEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + if (ALGO_TOTP != + crypto_getAlgoInfo(crypto_getCurrentAlgorithm(&appData->cryptoHandler))) { + appData->currentStates.base = XTOTP_DISPATCHER; + return; + } + frontend_createBaseWindow(appData, "TOTP", NULL); + appData->processData.lastNeededTime = 0; + showTOTP(appData, INPUT_STATE_LOW, + INPUT_NONE); // Process to force an update action + return; +} + +void showTOTP(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (INPUT_STATE_RISING_EDGE == inputState && INPUT_0 == inputButton) { + appData->currentStates.base = XTOTP_GO_TO_SETTINGS; + return; + } + if (appData->processData.lastNeededTime >= XTOTP_SAMPLE_TIME) { + appData->processData.lastNeededTime -= XTOTP_SAMPLE_TIME; + if (0 == (appData->processData.lastNeededTime % 1000)) { + // Update time + char menuTitleBuffer[16]; + snprintf(menuTitleBuffer, sizeof(menuTitleBuffer), "%3d TOTP", + (uint16_t)MS_TO_SECOND(appData->processData.lastNeededTime)); + frontend_updateMenuTitle(appData, menuTitleBuffer); + appData->iDisplay.show(); + } + } else { + // Update Passcode + appData->iClock.powerOn(); + uint64_t currentTime = appData->iClock.getUnixTime(); + if (0 == currentTime) { + LOGGER_ERROR("Could not get the current time"); + return; + } + LOGGER_DEBUG("Received time: %lu", currentTime); + appData->iClock.powerOff(); + + xtotpAlgoSettingsType *usedSecret = + crypto_getCurrentAlgorithm(&appData->cryptoHandler); + + appData->processData.lastCode = crypto_calc_OTP( + &appData->cryptoHandler, &appData->iCrypto, currentTime, 0); + appData->processData.lastNeededTime = + SECOND_TO_MS(usedSecret->interval - currentTime % usedSecret->interval); + + uint8_t halfCodeLength = usedSecret->digits / 2; + uint32_t decimalSeparator = power10(halfCodeLength); + LOGGER_INFO( + "Code generated %.*u %.*u", halfCodeLength, + (appData->processData.lastCode / decimalSeparator) % decimalSeparator, + halfCodeLength, appData->processData.lastCode % decimalSeparator); + frontend_updateDatafield( + appData, FRONTEND_DATAFIELD_CENTRED, "%.*u %.*u", halfCodeLength, + (appData->processData.lastCode / decimalSeparator) % decimalSeparator, + halfCodeLength, appData->processData.lastCode % decimalSeparator); + + char menuTitleBuffer[16]; + snprintf(menuTitleBuffer, sizeof(menuTitleBuffer), "%3d TOTP", + (uint16_t)MS_TO_SECOND(appData->processData.lastNeededTime)); + frontend_updateMenuTitle(appData, menuTitleBuffer); + appData->iDisplay.show(); + } +} + +void goToSettingsEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + frontend_createBaseWindow(appData, "Settings?", NULL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "%c Return", + ARROW_LEFT_SYMBOL); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, + "Enter %c Settings", ARROW_RIGHT_SYMBOL); + appData->iDisplay.show(); + return; +} +void goToSettings(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + if (INPUT_STATE_RISING_EDGE == inputState) { + switch (inputButton) { + case INPUT_DEL: + appData->currentStates.base = XTOTP_DISPATCHER; + break; + case INPUT_ENTER: + appData->currentStates.base = XTOTP_SETTINGS; + break; + default: + break; + } + } + return; +} + +void settingsEntry(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + appData->currentStates.settings = SETTINGS_ENTRY; + appData->currentStates.additionalSettings = ADDITIONAL_MAX_STATES; + appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_MAX_STATES; + frontend_createBaseWindow(appData, "Settings", NULL); + xtotpAlgoSettingsType *selectedAlg = + &appData->cryptoHandler.algorithms[appData->cryptoHandler.usedAlgorithm]; + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "%3d%c%us%1uD%s", + appData->cryptoHandler.usedAlgorithm, + ARROW_RIGHT_SYMBOL, selectedAlg->interval, + selectedAlg->digits, + crypto_getAlgoName(selectedAlg)); + frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "0 %c Additional", + ARROW_RIGHT_SYMBOL); + appData->iDisplay.show(); + return; +} + +void settings(xtotp_Data *appData, + inputLevelState inputState, + inputButtonPos inputButton) +{ + (void)inputState; + (void)inputButton; + + xtotp_SettingStates oldSettings = appData->currentStates.settings; + + switch (appData->currentStates.settings) { + case SETTINGS_ENTRY: + if (INPUT_STATE_RISING_EDGE == inputState) { + switch (inputButton) { + case INPUT_DEL: + appData->currentStates.base = XTOTP_DISPATCHER; + break; + case INPUT_ENTER: + appData->currentStates.base = XTOTP_DISPATCHER; + break; + case INPUT_0: + appData->currentStates.settings = SETTINGS_ADDITIONAL; + settings_processAdditional(appData, INPUT_STATE_LOW, INPUT_NONE); + break; + default: + if (inputButton > INPUT_0 && inputButton <= INPUT_9) { + uint64_t readInput = 0; + input_readNumber(&readInput, XTOTP_STORABLE_SECRETS, inputButton); + appData->cryptoHandler.processedAlgorithm = (uint8_t)readInput; + frontend_showSecretState(appData); + appData->iDisplay.show(); + appData->currentStates.settings = SETTINGS_SELECT_KEY; + } + break; + } + } + break; + case SETTINGS_SELECT_KEY: + if (INPUT_STATE_RISING_EDGE == inputState) { + uint64_t readInput = (uint64_t)appData->cryptoHandler.processedAlgorithm; + if (input_readNumber((uint64_t *)&readInput, XTOTP_STORABLE_SECRETS, + inputButton)) { + if (appData->cryptoHandler.processedAlgorithm > 0 && + appData->cryptoHandler.processedAlgorithm <= + XTOTP_STORABLE_SECRETS) { + appData->currentStates.settings = SETTINGS_REGISTER_KEY; + settings_processSecretHandling(appData, INPUT_STATE_LOW, INPUT_NONE); + break; + } else { + appData->cryptoHandler.processedAlgorithm = 0; + } + } + appData->cryptoHandler.processedAlgorithm = (uint8_t)readInput; + if (INPUT_DEL == inputButton && 0 == readInput) { + appData->currentStates.settings = SETTINGS_ENTRY; + break; + } + frontend_showSecretState(appData); + appData->iDisplay.show(); + } + break; + case SETTINGS_REGISTER_KEY: + settings_processSecretHandling(appData, inputState, inputButton); + break; + case SETTINGS_ADDITIONAL: + settings_processAdditional(appData, inputState, inputButton); + break; + default: + appData->currentStates.settings = SETTINGS_ENTRY; + return; + } + if (oldSettings != appData->currentStates.settings && + SETTINGS_ENTRY == appData->currentStates.settings) { + settingsEntry(appData, inputState, inputButton); + } +} diff --git a/xotp_app/app/src/xtotpUtil.c b/xotp_app/app/src/xtotpUtil.c @@ -0,0 +1,51 @@ +/** + * @file xtotpUtil.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Includes some help functions and important defines + * @version 0.1 + * @date 27-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "xtotpUtil.h" + +#include <time.h> + +uint32_t power10(const uint8_t exponent) { + uint32_t result = 1; + for (uint8_t i = 0; i < exponent; i++) { + result *= 10; + } + return result; +} +uint8_t base10Exponent(uint32_t number) { + uint8_t exponent = 0; + while (number / 10 != 0) { + number /= 10; + exponent++; + } + return exponent; +} + +uint32_t getTickDiff(uint32_t newTick, uint32_t oldTick) { + // Check no overflow + if (newTick >= oldTick) { + return newTick - oldTick; + } else { + return UINT32_MAX - oldTick + newTick; + } +} +\ No newline at end of file diff --git a/xotp_app/interfaces/inc/autoPayInterface.h b/xotp_app/interfaces/inc/autoPayInterface.h @@ -0,0 +1,109 @@ +/** + * @file autoPayInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Autopay interface to automated payment without user interaction + * @version 0.1 + * @date 29-07-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef AUTOPAY_INTERFACE_H +#define AUTOPAY_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <xtotpConfig.h> + +/** + * @brief Callback type for power control functions. + */ +typedef void (*autoPayPwrCB)(void); + +/** + * @brief Callback type to retrieve received codes. + * + * @param receivedPasscodes Pointer where the received codes will + * be stored. It as the size of @ref XTOTP_AUTOPAY_RECEIVED_PASSKEYS. + * @return uint8_t 0 for nothing received, 1 for received passkeys. + */ +typedef uint8_t (*autoPayGetPasscodes)(uint32_t *receivedPasscodes); + +/** + * @brief Callback to check if passcodes are received + * + * @return True if passcodes are received, false otherwise + */ +typedef bool (*autoPayArePasscodesReceived)(void); + +/** + * @brief Callback type to send a formatted URL. + * + * @param format Format string (similar to printf). + * @param ... Additional arguments for the format string. + * @return uint8_t Status code (e.g., 0 for success, non-zero for error). + */ +typedef uint8_t (*autoPaySendURL)(const char *format, ...); + +/** + * @brief Callback type to check if the URL has been read. + * + * @return True if the URL is read, false otherwise. + */ +typedef bool (*autoPayIsURLRead)(void); + +/** + * @brief Structure representing the AutoPay interface. + */ +typedef struct { + autoPayPwrCB powerOn; ///< Callback to power on the module. + autoPayPwrCB powerOff; ///< Callback to power off the module. + autoPayGetPasscodes getPasscodes; ///< Callback to retrieve received passkeys. + autoPayArePasscodesReceived + arePasscodesReceived; ///< Callback to ceck if passcodes are received + autoPaySendURL sendURL; ///< Callback to send a formatted URL. + autoPayIsURLRead isURLRead; ///< Callback to read if URL is read + uint8_t transmitState; ///< Saves the current transmit state +} autoPayInterface; + +/** + * @brief Initializes the AutoPay interface with the provided callbacks. + * + * @param interfaceHandler Pointer to the AutoPay interface structure to + * initialize. + * @param powerOnCB Callback for powering on the module. + * @param powerOffCB Callback for powering off the module. + * @param getCodesCB Callback for retrieving received codes. + * @param sendURLCB Callback for sending a formatted URL. + * @param isURLReadCB Callback for checking if the url is read + */ +uint8_t +autoPay_initInterface(autoPayInterface *interfaceHandler, + autoPayPwrCB powerOnCB, + autoPayPwrCB powerOffCB, + autoPayGetPasscodes getPasscodesCB, + autoPayArePasscodesReceived arePasscodesReceivedCB, + autoPaySendURL sendURLCB, + autoPayIsURLRead isURLReadCB); + +#ifdef __cplusplus +} +#endif + +#endif /* AUTOPAY_INTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/batMeasInterface.h b/xotp_app/interfaces/inc/batMeasInterface.h @@ -0,0 +1,142 @@ +/** + * @file batMeasInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Battery measurement interface to get the current state in percent + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef BAT_MEAS_INTERFACE_H +#define BAT_MEAS_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> + +#define BATTERY_PERCENT_AVERAGE_SIZE \ + (5) ///< Size of stored battery percent states + +/** + * @brief Battery status + * + */ +typedef enum { + BAT_DISCHARGE, + BAT_CHARGING, + BAT_FULL, + BAT_UNKNOWN +} bat_status_type; + +/** + * @brief Power on and off callback prototype + * + */ +typedef void (*batMeasPwrCB)(void); + +/** + * @brief Battery status callback prototype. + * + * 1. Param: Battery state in percent + * 2. Param: Battery voltage state + * 3. Param: Battery charging status + */ +typedef uint8_t (*getBatteryStateCB)(uint8_t *, uint16_t *, bat_status_type *); + +/** + * @brief Battery measurement interface + * + */ +typedef struct { + /* User Callbacks */ + batMeasPwrCB powerOn; ///< Enables the measurement + batMeasPwrCB powerOff; ///< Disables the measurement + getBatteryStateCB + getBatteryState; ///< Get the current battery state in percent, the + ///< absolute value in mV and the charging state + /* Battery state values */ + uint8_t + averageValues[BATTERY_PERCENT_AVERAGE_SIZE]; ///< Last stored values used + ///< for get an average + + uint8_t storedValues; ///< Number of stored values in average buffer + uint16_t voltageValue; ///< last measured voltage states + uint32_t lastUpdateTime; ///< Last update time in ticks + bat_status_type chargingStatus; ///< Charging status + bool isEnabled; +} batMeasInterface; + +/** + * @brief Initialise the batMeas inteface handler with the callbacks + * + * @param interfaceHandler Pointer to the battery measurement interface handler. + * @param powerOnCB Callback to enable the battery measurement + * @param powerOffCB Callback to disable the battery measurement + * @param getStateCB Callback to get the current state of the battery + * @return uint8_t EXIT_SUCCESS, EXIT_FAILURE otherwise + */ +uint8_t batMeas_initInterface(batMeasInterface *interfaceHandler, + batMeasPwrCB powerOnCB, + batMeasPwrCB powerOffCB, + getBatteryStateCB getStateCB); + +/** + * @brief Measures the current battery state. + * + * This function triggers a battery measurement if the specified update interval + * has elapsed since the last measurement. It updates the internal state of the + * battery interface. + * + * @param interfaceHandler Pointer to the battery measurement interface handler. + * @param currentTick Current system tick (e.g., from a timer or RTOS). + * @param updateTime Minimum time interval (in ticks) between measurements. + * @return uint8_t Returns 1 if a new measurement was performed, 0 otherwise. + */ +uint8_t batMeas_measure(batMeasInterface *interfaceHandler, + uint32_t currentTick, + uint32_t updateTime); + +/** + * @brief Retrieves the current battery charge level as a percentage. + * + * Returns the last measured battery level in percent (0–100%). + * + * @param interfaceHandler Pointer to the battery measurement interface handler. + * @return uint8_t Battery charge level in percent. + */ +uint8_t +batMeas_getBatteryPercentState(const batMeasInterface *interfaceHandler); + +/** + * @brief Retrieves the current battery voltage. + * + * Returns the last measured battery voltage in millivolts. + * + * @param interfaceHandler Pointer to the battery measurement interface handler. + * @return uint16_t Battery voltage in millivolts. + */ +uint16_t +batMeas_getBatteryVoltageState(const batMeasInterface *interfaceHandler); + +#ifdef __cplusplus +} +#endif + +#endif /* BAT_MEAS_INTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/clockInterface.h b/xotp_app/interfaces/inc/clockInterface.h @@ -0,0 +1,87 @@ +/** + * @file clockInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Clock interface class to get and set the current time from the device + * time. + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef CLOCK_INTERFACE_H +#define CLOCK_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/** + * @brief Clock power callback function + * + */ +typedef void (*clockPwrCB)(void); + +/** + * @brief Clock get current time callback function + * + * @return Returns the current time, 0 in case of an error + * + */ +typedef uint64_t (*clockGetTimeCB)(void); + +/** + * @brief Set the current time to the clock + * @param uint64_t the time to set + * + */ +typedef uint8_t (*clockSetTimeCB)(uint64_t); + +/** + * @brief Clock interface handler + * + */ +typedef struct { + uint64_t lastUpdateTime; ///< Saves the last time when setUnixTime was used + clockPwrCB powerOn; ///< Enables the clock + clockPwrCB powerOff; ///< Disables the clock + clockGetTimeCB getUnixTime; ///< Get the current unix time + clockSetTimeCB setUnixTime; ///< Set (synchronize) the current unix time +} clockInterface; + +/** + * @brief Initialise the clock interface + * + * @param interfaceHandler The clock interface structure to initialise + * @param powerOnCB Clock power on callback + * @param powerOffCB Clock power off callback + * @param getUnixTimeCB Clock get time callback + * @param setUnixTimeCB Clock set time callback + * @return uint8_t EXIT_SUCCESS in success, EXIT_FAILURE otherwise + */ +uint8_t clock_initInterface(clockInterface *interfaceHandler, + clockPwrCB powerOnCB, + clockPwrCB powerOffCB, + clockGetTimeCB getUnixTimeCB, + clockSetTimeCB setUnixTimeCB); + +#ifdef __cplusplus +} +#endif + +#endif /* CLOCK_INTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/cryptoInterface.h b/xotp_app/interfaces/inc/cryptoInterface.h @@ -0,0 +1,109 @@ +/** + * @file cryptoInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Crypto interface to calculate the hash codes with the desired hash + * methods + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#ifndef CRYPTO_INTERFACE_H +#define CRYPTO_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#include "talerAmount.h" +#include "xtotpConfig.h" +#include "xtotpCryptoHandler.h" + +/** + * @brief Callback type for computing a TOTP value. + * + * This function computes a Time-based One-Time Password (TOTP) based on the + * given time and algorithm settings. + * + * @param time The current time in seconds since the Unix epoch. + * @param algoSettings Pointer to the algorithm settings used for TOTP + * computation. + * @return uint32_t The computed TOTP value. + */ +typedef uint32_t (*cryptoCalcTotpCB)(const uint64_t time, + const xtotpAlgoSettingsType *algoSettings); + +/** + * @brief Callback type for computing an extended TOTP (X-TOTP) value. + * + * This function computes an X-TOTP value, which includes additional input such + * as a monetary amount. + * + * @param time The current time in seconds since the Unix epoch. + * @param algoSettings Pointer to the algorithm settings used for X-TOTP + * computation. + * @param amount Pointer to the amount structure used in the computation. + * @return uint32_t The computed X-TOTP value. + */ +typedef uint32_t (*cryptoCalcXTotpCB)(const uint64_t time, + const xtotpAlgoSettingsType *algoSettings, + const TALER_AmountNBO *amount); + +/** + * @brief Structure representing the cryptographic interface for TOTP and X-TOTP + * computation. + */ +typedef struct { + cryptoCalcTotpCB computeTotp; ///< Callback for computing a TOTP value. */ + cryptoCalcXTotpCB + computeXTotp; ///< Callback for computing an X-TOTP value. */ +} cryptoInterface; + +/** + * @brief Initializes the crypto interface with the provided callback functions. + * + * @param interfaceHandler Pointer to the cryptoInterface structure to + * initialize. + * @param computeTotpCB Callback function for TOTP computation. + * @param computeXTotpCB Callback function for X-TOTP computation. + * @return uint8_t Status code (e.g., 0 for success, non-zero for error). + */ +uint8_t crypto_initInterface(cryptoInterface *interfaceHandler, + cryptoCalcTotpCB computeTotpCB, + cryptoCalcXTotpCB computeXTotpCB); + +/** + * @brief Calculate with the algorithm the passkey with the given time + * + * @param cryptoHandler The crypto handler where the needed information are + * stored + * @param cryptoInterface THe crpyto interface for hardware independent code + * @param currentTime The current time + * @param amount Add amount if needed for xTOTP, use 0 in case of base TOTP + * @return uint32_t the generated OTP passkey + */ +uint32_t crypto_calc_OTP(xtotpCryptoHandler *cryptoHandler, + cryptoInterface *iCrypto, + uint64_t currentTime, + uint64_t amount); + +#ifdef __cplusplus +} +#endif + +#endif /* CRYPTO_INTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/deviceInterface.h b/xotp_app/interfaces/inc/deviceInterface.h @@ -0,0 +1,75 @@ +/** + * @file deviceInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief device interface to get the current tick to poll events and to + * shutdown the device The tick is used to poll in a desired frequency and the + * shutdown is needed to move the device in a low energy part. All components + * are set to power off before calling this function. + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef DEVICE_INTERFACE_H +#define DEVICE_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/** + * @brief Device shutdown callback + * + */ +typedef void (*deviceShutdownCB)(void); + +/** + * @brief Device getTick callback + * + */ +typedef uint32_t (*deviceGetTickCB)(void); + +/** + * @brief Device interface handler + * + */ +typedef struct { + deviceGetTickCB getTick; ///< Get the current system tick for polling + deviceShutdownCB + shutdown; ///< Moves the device in low energy consumption mode, expect to + ///< stay in this function during shutdown +} deviceInterface; + +/** + * @brief Initialise the device interface structure + * + * @param interfaceHandler The deviceInterface structure to initialise + * @param getTickCB getTick callback function + * @param shutdownCB shutdown callback function + * @return uint8_t EXIT_SUCCESS in success, EXIT_FAILURE otherwise + */ +uint8_t device_initInterface(deviceInterface *interfaceHandler, + deviceGetTickCB getTickCB, + deviceShutdownCB shutdownCB); + +#ifdef __cplusplus +} +#endif + +#endif /* DEVICEINTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/displayInterface.h b/xotp_app/interfaces/inc/displayInterface.h @@ -0,0 +1,101 @@ +/** + * @file displayInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Display interface to show the menu content on it + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef DISPLAY_INTERFACE_H +#define DISPLAY_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "pbm_fontHandler.h" +#include "pbm_types.h" +#include <stdint.h> + +/** + * @brief Display power callback prototype + * + */ +typedef void (*displayPwrCB)(void); + +/** + * @brief Display show data callback + * + */ +typedef uint32_t (*displayShowCB)(void); + +/** + * @brief Display interface handler + * + */ +typedef struct { + /* Image references */ + pbm_image *image; ///< Pointer to an image structure to handle the frontend + pbm_font *dispFontSmall; ///< Pointer to the used small font + pbm_font *dispFontBig; ///< Pointer to the display big font + /* Callback functions */ + displayPwrCB powerOn; ///< Power on function + displayPwrCB powerOff; ///< Power off function + displayShowCB show; ///< Display show (update screen) + /* Symbol font indexes */ + uint8_t symbolCharge; ///< Charge symbol + uint8_t symbolSynchronize; ///< Synchronising symbol + uint8_t symbolBatteryStart; ///< Symbol update battery start with expected 4 + ///< followed smaller states + /* Theme */ + pbm_colors theme; ///< Current display themes +} displayInterface; + +/** + * @brief Initialise the display interface structure + * + * @param interfaceHandler The structure as reference to initialise + * @param displayImage Display pbm-image + * @param smallFont Small font used for frontend + * @param bigFont Big font reference used for frontend + * @param powerOnCB Power on callback + * @param powerOffCB Power off callback + * @param showCB image display data to the display + * @param chargeSymbolIndex Charge symbol + * @param synchronizeSymbolIndex Sync symbol + * @param batteryStartIndex Battery start with expected 4 followed symbols + * @param theme The current display theme + * @return uint8_t EXIT_SUCCESS in success, EXIT_FAILURE otherwise + */ +uint8_t display_initInterface(displayInterface *interfaceHandler, + pbm_image *displayImage, + pbm_font *smallFont, + pbm_font *bigFont, + displayPwrCB powerOnCB, + displayPwrCB powerOffCB, + displayShowCB showCB, + uint8_t chargeSymbolIndex, + uint8_t synchronizeSymbolIndex, + uint8_t batteryStartIndex, + pbm_colors theme); + +#ifdef __cplusplus +} +#endif + +#endif /* DISPLAY_INTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/inputInterface.h b/xotp_app/interfaces/inc/inputInterface.h @@ -0,0 +1,134 @@ +/** + * @file inputInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Interface to read out the keyboard from the current hardware + * @version 0.1 + * @date 13-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef INPUT_INTERFACE_H +#define INPUT_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> + +/** + * @brief Input level states + * + */ +typedef enum { + INPUT_STATE_LOW = 0, ///< Button not pressed + INPUT_STATE_RISING_EDGE, ///< Rising edge on the current button + INPUT_STATE_FALLING_EDGE, ///< Falling edge on the current button + INPUT_STATE_HIGH ///< Button pressed +} inputLevelState; + +/** + * @brief Input positions + * + */ +typedef enum { + INPUT_0 = 0, + INPUT_1, + INPUT_2, + INPUT_3, + INPUT_4, + INPUT_5, + INPUT_6, + INPUT_7, + INPUT_8, + INPUT_9, + INPUT_DEL, + INPUT_ENTER, + INPUT_NONE, +} inputButtonPos; + +/** + * @brief Callback type for powering on or off input hardware. + */ +typedef void (*inputPwrCB)(void); + +/** + * @brief Callback type for sampling input state. + * + * @param inputButton Pointer to the button position to be sampled. + * @return inputLevelState The current level state of the input. + */ +typedef inputLevelState (*inputSampleCB)(inputButtonPos *); + +/** + * @brief Interface structure for input handling. + */ +typedef struct { + inputPwrCB powerOn; /**< Callback to power on the input system. */ + inputPwrCB powerOff; /**< Callback to power off the input system. */ + inputSampleCB readInput; /**< Callback to read the current input state. */ +} inputInterface; + +/** + * @brief Initializes the input interface with provided callbacks. + * + * @param interfaceHandler Pointer to the input interface structure to + * initialize. + * @param powerOnCB Callback for powering on the input system. + * @param powerOffCB Callback for powering off the input system. + * @param readInputCB Callback for reading input state. + * @return uint8_t Returns 0 on success, non-zero on failure. + */ +uint8_t input_initInterface(inputInterface *interfaceHandler, + inputPwrCB powerOnCB, + inputPwrCB powerOffCB, + inputSampleCB readInputCB); + +/** + * @brief Reads a numeric input from a button press. + * + * @param number Pointer to store the resulting number. + * @param maxLength Maximum number of digits to read. + * @param inputButton The button position to read from. + * @return true if a valid number was read, false otherwise. + */ +bool input_readNumber(uint64_t *number, + uint32_t maxLength, + const inputButtonPos inputButton); + +/** + * @brief Reads a base-8 encoded input string from button presses. + * + * @param inputButton The button position to read from. + * @param inputString Pointer to the buffer storing the input string. + * @param inputLength Pointer to store the length of the input string. + * @param stringMaxLength Maximum allowed length of the input string. + * @param clearBuffer If true, clears the buffer before reading. + * @return true if input was successfully read, false otherwise. + */ +bool input_readBase8(const inputButtonPos inputButton, + uint8_t **inputString, + uint8_t *inputLength, + uint8_t stringMaxLength, + bool clearBuffer); + +#ifdef __cplusplus +} +#endif + +#endif /* INPUT_INTERFACE_H*/ diff --git a/xotp_app/interfaces/inc/syncInterface.h b/xotp_app/interfaces/inc/syncInterface.h @@ -0,0 +1,129 @@ +/** + * @file syncInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief synchronisation interface to get a valid reference time + * @version 0.1 + * @date 12-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef SYNC_INTERFACE_H +#define SYNC_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include "tinytime.h" + +/** + * @brief Sync status + * + */ +typedef enum { + SYNC_TIME_AND_DATE = 0, ///< Time received to synchronize + SYNC_TIME_ONLY, ///< Received time only + SYNC_WAITING, ///< Connection OK, but no time received + SYNC_ERROR ///< Error in connection +} syncStatus; + +/** + * @brief Sync power callback prototype + * + */ +typedef void (*syncPwrCB)(void); + +/** + * @brief Sync synchronise callback prototype + * @param tinyTimeType the crrent time if received + * + * @return the sync status + */ +typedef syncStatus (*synchronizeCB)(tinyTimeType *); + +/** + * @brief Sync interface handler + * + */ +typedef struct { + /* Callbacks */ + syncPwrCB powerOn; + syncPwrCB powerOff; + synchronizeCB synchronize; + /* Used data */ + uint64_t lastSyncTime; ///< Last synchronize time + uint64_t expectedSyncTime; ///< The expected next update process + uint64_t syncDiff; ///< Last difference between clock and synchronization time + uint32_t syncDuration; ///< Current synchronisation duration in ms + bool isSynchronizing; ///< Flag that the sync process is running +} syncInterface; + +/** + * @brief Initializes the synchronization interface. + * + * Sets up the synchronization interface with the provided callback functions + * and the last known synchronization time. This function prepares the interface + * for synchronization events. + * + * @param interfaceHandler Pointer to the synchronization interface structure to + * initialize. + * @param powerOnCB Callback function to be called when powering on. + * @param powerOffCB Callback function to be called when powering off. + * @param syncCB Callback function to be called during synchronization. + * @param lastSyncTime Timestamp of the last synchronization event (in + * milliseconds or ticks). + * @return uint8_t Returns EXIT_SUCCESS if initialization was successful, + * EXIT_FAILURE otherwise. + */ +uint8_t sync_initInterface(syncInterface *interfaceHandler, + syncPwrCB powerOnCB, + syncPwrCB powerOffCB, + synchronizeCB syncCB, + uint64_t lastSyncTime); + +/** + * @brief Sync enable hardware + * + * @param interfaceHandler the sync interface handler reference + */ +void sync_enable(syncInterface *interfaceHandler); + +/** + * @brief Stop synchronising + * + * @param interfaceHandler the sync interface handler reference + */ +void sync_abort(syncInterface *interfaceHandler); + +/** + * @brief Check the synchronisung function to recieve the time + * + * @param interfaceHandler the sync interface handler reference + * @return the sync status + + */ +syncStatus sync_synchronize(syncInterface *interfaceHandler); + +#ifdef __cplusplus +} +#endif + +#endif /* SYNC_INTERFACE_H*/ diff --git a/xotp_app/interfaces/src/autoPayInterface.c b/xotp_app/interfaces/src/autoPayInterface.c @@ -0,0 +1,47 @@ +/** + * @file autoPayInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief + * @version 0.1 + * @date 03-08-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#include "autoPayInterface.h" +#include <stdlib.h> + +uint8_t +autoPay_initInterface(autoPayInterface *interfaceHandler, + autoPayPwrCB powerOnCB, + autoPayPwrCB powerOffCB, + autoPayGetPasscodes getPasscodesCB, + autoPayArePasscodesReceived arePasscodesReceivedCB, + autoPaySendURL sendURLCB, + autoPayIsURLRead isURLReadCB) +{ + if (NULL == interfaceHandler || NULL == powerOnCB || NULL == powerOffCB || + NULL == getPasscodesCB || NULL == arePasscodesReceivedCB || + NULL == sendURLCB || NULL == isURLReadCB) { + return EXIT_FAILURE; + } + interfaceHandler->powerOn = powerOnCB; + interfaceHandler->powerOff = powerOffCB; + interfaceHandler->getPasscodes = getPasscodesCB; + interfaceHandler->arePasscodesReceived = arePasscodesReceivedCB; + interfaceHandler->sendURL = sendURLCB; + interfaceHandler->isURLRead = isURLReadCB; + return EXIT_SUCCESS; +} diff --git a/xotp_app/interfaces/src/batMeasInterface.c b/xotp_app/interfaces/src/batMeasInterface.c @@ -0,0 +1,136 @@ +/** + * @file batMeasInterface.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Battery measurement interface to get the current state in percent + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "batMeasInterface.h" + +#include <stdlib.h> +#include <string.h> + +#include "logger.h" +#include "xtotpUtil.h" + +uint8_t batMeas_initInterface(batMeasInterface *interfaceHandler, + batMeasPwrCB powerOnCB, + batMeasPwrCB powerOffCB, + getBatteryStateCB getStateCB) +{ + if (NULL == interfaceHandler || NULL == powerOnCB || NULL == powerOffCB || + NULL == getStateCB) { + return EXIT_FAILURE; + } + // Add callback functions + interfaceHandler->powerOn = powerOnCB; + interfaceHandler->powerOff = powerOffCB; + interfaceHandler->getBatteryState = getStateCB; + + // Init values + memset(interfaceHandler->averageValues, 0, BATTERY_PERCENT_AVERAGE_SIZE); + interfaceHandler->lastUpdateTime = 0; + interfaceHandler->storedValues = 0; + interfaceHandler->voltageValue = 0; + interfaceHandler->isEnabled = false; + return EXIT_SUCCESS; +} + +uint8_t batMeas_measure(batMeasInterface *interfaceHandler, + uint32_t currentTick, + uint32_t updateTime) +{ + if (NULL == interfaceHandler) { + return EXIT_FAILURE; + } + // Check battery voltage is needed + if (interfaceHandler->storedValues < BATTERY_PERCENT_AVERAGE_SIZE || + getTickDiff(currentTick, interfaceHandler->lastUpdateTime) >= + updateTime) { + // Check measurement must be enabled + if (!interfaceHandler->isEnabled) { + interfaceHandler->powerOn(); + interfaceHandler->isEnabled = true; + return EXIT_FAILURE; + } + // Get new value + uint8_t newBatValue = 0; + uint16_t newBatVoltageValue = 0; + + // Get new values, return in case of error + if (interfaceHandler->getBatteryState(&newBatValue, &newBatVoltageValue, + &interfaceHandler->chargingStatus)) { + return EXIT_FAILURE; + } + // Correct values + if (interfaceHandler->storedValues >= BATTERY_PERCENT_AVERAGE_SIZE) { + // Update list + for (uint8_t i = BATTERY_PERCENT_AVERAGE_SIZE - 1; i; i--) { + interfaceHandler->averageValues[i] = + interfaceHandler->averageValues[i - 1]; + } + interfaceHandler->averageValues[0] = newBatValue; + + } else { + // Increment new incremented value + interfaceHandler->storedValues++; + interfaceHandler->averageValues[BATTERY_PERCENT_AVERAGE_SIZE - + interfaceHandler->storedValues] = + newBatValue; + } + interfaceHandler->voltageValue = newBatVoltageValue; + LOGGER_DEBUG("Current battery level: %u%%", newBatValue); + interfaceHandler->lastUpdateTime = currentTick; + interfaceHandler->powerOff(); + interfaceHandler->isEnabled = false; + return EXIT_SUCCESS; + } else { + bat_status_type newState = interfaceHandler->chargingStatus; + interfaceHandler->getBatteryState(NULL, NULL, &newState); + if (newState != interfaceHandler->chargingStatus) { + interfaceHandler->chargingStatus = newState; + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } + } +} + +uint8_t batMeas_getBatteryPercentState(const batMeasInterface *interfaceHandler) +{ + if (NULL == interfaceHandler || 0 == interfaceHandler->storedValues) { + return 0; + } + uint16_t sumState = 0; + for (uint8_t i = + BATTERY_PERCENT_AVERAGE_SIZE - interfaceHandler->storedValues; + i < BATTERY_PERCENT_AVERAGE_SIZE; i++) { + sumState += interfaceHandler->averageValues[i]; + } + return (uint8_t)(sumState / interfaceHandler->storedValues); +} + +uint16_t +batMeas_getBatteryVoltageState(const batMeasInterface *interfaceHandler) +{ + if (NULL == interfaceHandler) { + return 0; + } + return interfaceHandler->voltageValue; +} +\ No newline at end of file diff --git a/xotp_app/interfaces/src/clockInterface.c b/xotp_app/interfaces/src/clockInterface.c @@ -0,0 +1,44 @@ +/** + * @file clockInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Clock interface class to get and set the current time from the device + * time. + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "clockInterface.h" +#include <stdlib.h> + +uint8_t clock_initInterface(clockInterface *interfaceHandler, + clockPwrCB powerOnCB, + clockPwrCB powerOffCB, + clockGetTimeCB getUnixTimeCB, + clockSetTimeCB setUnixTimeCB) +{ + if (NULL == interfaceHandler || NULL == powerOnCB || NULL == powerOffCB || + NULL == getUnixTimeCB || NULL == setUnixTimeCB) { + return EXIT_FAILURE; + } + interfaceHandler->powerOn = powerOnCB; + interfaceHandler->powerOff = powerOffCB; + interfaceHandler->getUnixTime = getUnixTimeCB; + interfaceHandler->setUnixTime = setUnixTimeCB; + interfaceHandler->lastUpdateTime = 0; + return EXIT_SUCCESS; +} +\ No newline at end of file diff --git a/xotp_app/interfaces/src/cryptoInterface.c b/xotp_app/interfaces/src/cryptoInterface.c @@ -0,0 +1,67 @@ +/** + * @file cryptoInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Crypto interface to calculate the hash codes with the desired hash + * methods + * @version 0.1 + * @date 19-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ +#include "cryptoInterface.h" + +#include <stdlib.h> +#include <string.h> + +uint8_t crypto_initInterface(cryptoInterface *interfaceHandler, + cryptoCalcTotpCB computeTotpCB, + cryptoCalcXTotpCB computeXTotpCB) +{ + if (NULL == interfaceHandler || NULL == computeTotpCB || + NULL == computeXTotpCB) { + return EXIT_FAILURE; + } + // Register callbacks + interfaceHandler->computeTotp = computeTotpCB; + interfaceHandler->computeXTotp = computeXTotpCB; + return EXIT_SUCCESS; +} + +uint32_t crypto_calc_OTP(xtotpCryptoHandler *cryptoHandler, + cryptoInterface *iCrypto, + uint64_t currentTime, + uint64_t amount) +{ + if (NULL == cryptoHandler) { + return 0; + } + xtotpAlgoSettingsType *usedAlgorithm = + crypto_getCurrentAlgorithm(cryptoHandler); + switch (crypto_getAlgoInfo(usedAlgorithm)) { + case ALGO_UNINITIALIZED: + return 0; + case ALGO_TOTP: + return iCrypto->computeTotp(currentTime, usedAlgorithm); + case ALGO_XTOTP: { + TALER_AmountNBO talerAmount; + TALER_amount2AmountNBO(&talerAmount, &usedAlgorithm->xTalerData, amount); + // Get xTOTP code + return iCrypto->computeXTotp(currentTime, usedAlgorithm, &talerAmount); + } + default: + return 0; + } +} +\ No newline at end of file diff --git a/xotp_app/interfaces/src/deviceInterface.c b/xotp_app/interfaces/src/deviceInterface.c @@ -0,0 +1,40 @@ +/** + * @file deviceInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief device interface to get the current tick to poll events and to + * shutdown the device The tick is used to poll in a desired frequency and the + * shutdown is needed to move the device in a low energy part. All components + * are set to power off before calling this function. + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "deviceInterface.h" +#include <stdlib.h> + +uint8_t device_initInterface(deviceInterface *interfaceHandler, + deviceGetTickCB getTickCB, + deviceShutdownCB shutdownCB) +{ + if (NULL == interfaceHandler || NULL == getTickCB || NULL == shutdownCB) { + return EXIT_FAILURE; + } + interfaceHandler->getTick = getTickCB; + interfaceHandler->shutdown = shutdownCB; + return EXIT_SUCCESS; +} diff --git a/xotp_app/interfaces/src/displayInterface.c b/xotp_app/interfaces/src/displayInterface.c @@ -0,0 +1,60 @@ +/** + * @file displayInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Display interface to show the menu content on it + * @version 0.1 + * @date 17-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "displayInterface.h" +#include <stdlib.h> + +uint8_t display_initInterface(displayInterface *interfaceHandler, + pbm_image *displayImage, + pbm_font *smallFont, + pbm_font *bigFont, + displayPwrCB powerOnCB, + displayPwrCB powerOffCB, + displayShowCB showCB, + uint8_t chargeSymbolIndex, + uint8_t synchronizeSymbolIndex, + uint8_t batteryStartIndex, + pbm_colors theme) +{ + + if (NULL == interfaceHandler || NULL == displayImage || NULL == smallFont || + NULL == bigFont || NULL == powerOnCB || NULL == powerOffCB || + NULL == showCB) { + return EXIT_FAILURE; + } + /* Image references */ + interfaceHandler->image = displayImage; + interfaceHandler->dispFontSmall = smallFont; + interfaceHandler->dispFontBig = bigFont; + /* Callbacks */ + interfaceHandler->powerOn = powerOnCB; + interfaceHandler->powerOff = powerOffCB; + interfaceHandler->show = showCB; + /* Symbol indexes */ + interfaceHandler->symbolCharge = chargeSymbolIndex; + interfaceHandler->symbolSynchronize = synchronizeSymbolIndex; + interfaceHandler->symbolBatteryStart = batteryStartIndex; + /* Theme */ + interfaceHandler->theme = theme; + return EXIT_SUCCESS; +} +\ No newline at end of file diff --git a/xotp_app/interfaces/src/inputInterface.c b/xotp_app/interfaces/src/inputInterface.c @@ -0,0 +1,128 @@ +/** + * @file inputInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Interface to read out the keyboard from the current hardware + * @version 0.1 + * @date 13-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "inputInterface.h" + +#include "ringbuffer.h" +#include "xtotpUtil.h" + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#define STRING_BUFFER_MAX_SIZE (63) + +struct { + uint8_t buffer[STRING_BUFFER_MAX_SIZE]; + uint8_t position; +} input_stringBufferHandler; + +void clearStringBufferHandler(void); + +uint8_t input_initInterface(inputInterface *interfaceHandler, + inputPwrCB powerOnCB, + inputPwrCB powerOffCB, + inputSampleCB readInputCB) +{ + if (NULL == interfaceHandler || NULL == powerOnCB || NULL == powerOffCB || + NULL == readInputCB) { + return EXIT_FAILURE; + } + interfaceHandler->powerOn = powerOnCB; + interfaceHandler->powerOff = powerOffCB; + interfaceHandler->readInput = readInputCB; + + return EXIT_SUCCESS; +} + +bool input_readNumber(uint64_t *number, + uint32_t maxLength, + const inputButtonPos inputButton) +{ + if (NULL == number || inputButton >= INPUT_NONE) { + return false; + } + switch (inputButton) { + case INPUT_ENTER: + return (0 == *number) ? false : true; + case INPUT_DEL: + *number /= PAYMENT_BASE; + return false; + default: + if (*number < maxLength) { + *number *= PAYMENT_BASE; + *number += (uint64_t)inputButton; + if (*number > maxLength) { + *number = maxLength; + } + } + return false; + } +} + +bool input_readBase8(const inputButtonPos inputButton, + uint8_t **inputString, + uint8_t *inputLength, + uint8_t stringMaxLength, + bool clearBuffer) +{ + if (clearBuffer) { + clearStringBufferHandler(); + } + if (NULL == inputString) { + return false; + } + *inputString = input_stringBufferHandler.buffer; + switch (inputButton) { + case INPUT_ENTER: + if (input_stringBufferHandler.position > 0) { + *inputLength = input_stringBufferHandler.position; + return true; + } + break; + case INPUT_DEL: + input_stringBufferHandler.buffer[input_stringBufferHandler.position] = '\0'; + if (input_stringBufferHandler.position > 0) { + input_stringBufferHandler.position--; + } + break; + default: + if (inputButton >= INPUT_1 && inputButton <= INPUT_8) { + if (input_stringBufferHandler.position < + XTOTP_MIN((STRING_BUFFER_MAX_SIZE - 1), stringMaxLength)) { + input_stringBufferHandler.buffer[input_stringBufferHandler.position] = + inputButton + '0'; + input_stringBufferHandler.position++; + } + } + break; + } + input_stringBufferHandler.buffer[input_stringBufferHandler.position] = '\0'; + *inputLength = input_stringBufferHandler.position; + return false; +} + +void clearStringBufferHandler(void) +{ + memset(&input_stringBufferHandler, 0, sizeof(input_stringBufferHandler)); +} +\ No newline at end of file diff --git a/xotp_app/interfaces/src/syncInterface.c b/xotp_app/interfaces/src/syncInterface.c @@ -0,0 +1,70 @@ +/** + * @file syncInterface.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief synchronisation interface to get a valid reference time + * @version 0.1 + * @date 12-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "syncInterface.h" + +#include <stdint.h> +#include <stdlib.h> + +#include "xtotpConfig.h" +#include "xtotpUtil.h" + +uint8_t sync_initInterface( + syncInterface *interfaceHandler, + syncPwrCB powerOnCB, + syncPwrCB powerOffCB, + synchronizeCB syncCB, + uint64_t lastSyncTime) { + if (NULL == interfaceHandler || NULL == powerOnCB || NULL == powerOffCB || NULL == syncCB) { + return EXIT_FAILURE; + } + // Register callbacks and initial values + interfaceHandler->powerOn = powerOnCB; + interfaceHandler->powerOff = powerOffCB; + interfaceHandler->synchronize = syncCB; + interfaceHandler->lastSyncTime = lastSyncTime; + interfaceHandler->expectedSyncTime = lastSyncTime + XTOTP_SYNC_UPDATE_PERIOD; + + // Set all other data to 0 + interfaceHandler->isSynchronizing = false; + interfaceHandler->syncDuration = 0; + interfaceHandler->syncDiff = 0; + return EXIT_SUCCESS; +} + +void sync_enable(syncInterface *interfaceHandler) { + if (interfaceHandler->isSynchronizing) { + return; + } + interfaceHandler->powerOn(); + interfaceHandler->isSynchronizing = true; + interfaceHandler->syncDuration = 0; +} + +void sync_abort(syncInterface *interfaceHandler) { + if (!interfaceHandler->isSynchronizing) { + return; + } + interfaceHandler->powerOff(); + interfaceHandler->isSynchronizing = false; +} diff --git a/xotp_app/utils/inc/byteOrder.h b/xotp_app/utils/inc/byteOrder.h @@ -0,0 +1,61 @@ +/** + * @file byteOrder.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Changes the byte order from host to network endian order + * @version 0.1 + * @date 19-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef BYTEORDER_H +#define BYTEORDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/** + * @brief Changes the host 16 bit value to a netword byte order value + * + * @param h the host byte order value + * @return uint16_t the network byte ordered value + */ +uint16_t h2n16(uint16_t h); + +/** + * @brief Changes the host 32 bit value to a netword byte order value + * + * @param h the host byte order value + * @return uint16_t the network byte ordered value + */ +uint32_t h2n32(uint32_t h); + +/** + * @brief Changes the host 64 bit value to a netword byte order value + * + * @param h the host byte order value + * @return uint16_t the network byte ordered value + */ +uint64_t h2n64(uint64_t h); + +#ifdef __cplusplus +} +#endif + +#endif /* BYTEORDER_H*/ diff --git a/xotp_app/utils/inc/logger.h b/xotp_app/utils/inc/logger.h @@ -0,0 +1,103 @@ +/** + * @file logger.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Logger module to send debug and info messages + * @version 0.1 + * @date 2024-06-07 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef LOGGER_H +#define LOGGER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configured log levels + * + */ +typedef enum { + LOG_OFF = 0, + LOG_ERROR, + LOG_WARN, + LOG_INFO, + LOG_DEBUG, + LOG_TRACE +} logger_logLevel; + +#ifndef LOGGER_LEVEL +#define LOGGER_LEVEL (LOG_OFF) +#endif + +#if (LOGGER_LEVEL > 4) +#define LOGGER_TRACE(...) logger_sendMsg(LOG_TRACE, __VA_ARGS__) +#else +#define LOGGER_TRACE(...) \ + do { \ + ; \ + } while (0) + +#endif + +#if (LOGGER_LEVEL > 3) +#define LOGGER_DEBUG(...) logger_sendMsg(LOG_DEBUG, __VA_ARGS__) +#else +#define LOGGER_DEBUG(...) \ + do { \ + ; \ + } while (0) +#endif + +#if (LOGGER_LEVEL > 2) +#define LOGGER_INFO(...) logger_sendMsg(LOG_INFO, __VA_ARGS__) +#else +#define LOGGER_INFO(...) \ + do { \ + ; \ + } while (0) +#endif + +#if (LOGGER_LEVEL > 1) +#define LOGGER_WARN(...) logger_sendMsg(LOG_WARN, __VA_ARGS__) +#else +#define LOGGER_WARN(...) \ + do { \ + ; \ + } while (0) +#endif + +#if (LOGGER_LEVEL > 0) +#define LOGGER_ERROR(...) logger_sendMsg(LOG_ERROR, __VA_ARGS__) +#else +#define LOGGER_ERROR(...) \ + do { \ + ; \ + } while (0) +#endif + +typedef void (*logger_output)(const char *); + +void logger_init(logger_output outputCallback); + +void logger_sendMsg(logger_logLevel logLevel, const char *format, ...); + +#ifdef __cplusplus +} +#endif +#endif /* UART_LOGGER_H */ diff --git a/xotp_app/utils/inc/ringbuffer.h b/xotp_app/utils/inc/ringbuffer.h @@ -0,0 +1,143 @@ +/** + * @file ringbuffer.h + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Base ringbuffer header file for handling an unspecific data in a ring buffer design + * @version 0.2 + * @date 2024-03-15 + * @date 2025-02-13 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> + +/** + * @brief Ringbuffer The ring buffer handler structure + * + */ +typedef struct { + void *dataBuffer; ///< buffer array reference + uint32_t length; ///< array length of buffer + uint32_t headPos; ///< current head position (first element) + uint32_t tailPos; ///< current tail position, (where a new element can inserted) + uint32_t elmSize; ///< Size of an element in the buffer (use sizeof operator of an element) +} ringbuffer_handler; + +/** + * @brief Used to encode different errors. This StatusTypes are used to send back meaningful + * information to the caller of module functions. + * + */ +typedef enum { + RINGBUFFER_OK = 0, ///< No error + RINGBUFFER_OVERFLOW, ///< Buffer is full and no new data can be inserted + RINGBUFFER_EMPTY, ///< current ring buffer is empty + RINGBUFFER_ARGUMENT_ERROR, ///< Argument is not valid + RINGBUFFER_ERROR ///< Error in function or user copy callback function +} ringbuffer_StatusType; + +/** + * @brief Initialize a ring buffer structure + * + * @param rbHandler Reference to a ring buffer structure + * @param buffer Reference to the buffer array + * @param length The length of the buffer + * @param elmSize Size of an element in the buffer + * @param ccbHandler A function pointer to a element copy function + * @return Status code -- see ringbuffer_StatusType + */ +ringbuffer_StatusType ringbuffer_init(ringbuffer_handler *rbHandler, void *buffer, uint32_t length, uint32_t elmSize); + +/** + * @brief Destroy ring buffer structure + * + * @param rbHandler Reference to an initialized ring buffer structure. + * @return Address of the buffer. + * Note: Use the returned address to free the memory if it has been allocated on heap. + */ +void *ringbuffer_destroy(ringbuffer_handler *rbHandler); + +/** + * @brief Enqueue an element at the tail of the ring buffer. + * + * @param rbHandler Reference to the ring buffer structure + * @param data The data to insert into the ring buffer + * @return Status code -- see ringbuffer_StatusType + */ +ringbuffer_StatusType ringbuffer_enqueue(ringbuffer_handler *rbHandler, void *data); + +/** + * @brief Dequeue an element from the head of the ring buffer + * + * @param rbHandler Reference to the ringbuffer structure + * @param data The data which will be dequeued from the ringbuffer + * @return Status code -- see ringbuffer_StatusType + */ +ringbuffer_StatusType ringbuffer_dequeue(ringbuffer_handler *rbHandler, void **data); + +/** + * @brief Return the reference to the data at the head of the ring buffer without dequeuing it. + * + * @param rbHandler Reference to the ring buffer structure + * @return Data stored in the element at the head of the ring buffer, or NULL if the queue is empty. + */ +void *ringbuffer_peek(const ringbuffer_handler *rbHandler); + +/** + * @brief Number of elements stored in the ring buffer + * + * @param rbHandler Reference to the ring buffer structure + * @return uint32_t Number of elements in the ring buffer + */ +uint32_t ringbuffer_size(const ringbuffer_handler *rbHandler); + +/** + * @brief Check whether buffer is full + * + * @param rbHandler Reference to the ring buffer structure + * @return True if buffer is full, False otherwise + */ +bool ringbuffer_isFull(const ringbuffer_handler *rbHandler); + +/** + * @brief Check whether buffer is empty + * + * @param rbHandler Reference to the ring buffer structure + * @return True if buffer is empty, False otherwise + */ +bool ringbuffer_isEmpty(const ringbuffer_handler *rbHandler); + +/** + * @brief Clears the ringbuffer. Results to set tail and head index to the same level. + * + * @param rbHandler Reference to the ring buffer structure + * @return Status code -- see ringbuffer_StatusType + */ +ringbuffer_StatusType ringbuffer_clear(ringbuffer_handler *rbHandler); + +#ifdef __cplusplus +} +#endif + +#endif +\ No newline at end of file diff --git a/xotp_app/utils/src/byteOrder.c b/xotp_app/utils/src/byteOrder.c @@ -0,0 +1,36 @@ +/** + * @file byteOrder.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Changes the byte order from host to network endian order + * @version 0.1 + * @date 19-02-2025 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "byteOrder.h" + +uint16_t h2n16(uint16_t h) { + return __builtin_bswap16(h); +} + +uint32_t h2n32(uint32_t h) { + return __builtin_bswap32(h); +} + +uint64_t h2n64(uint64_t h) { + return __builtin_bswap64(h); +} diff --git a/xotp_app/utils/src/logger.c b/xotp_app/utils/src/logger.c @@ -0,0 +1,70 @@ +/** + * @file logger.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Logger module to send debug and info messages + * @version 0.1 + * @date 2024-06-07 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "logger.h" + +#define BUFFERSIZE (256) + +logger_output userCallback = NULL; + +const char *logLevelMsg[] = { + [LOG_OFF] = "LOGGER OFF", [LOG_ERROR] = "ERROR", [LOG_WARN] = "WARN", + [LOG_INFO] = "INFO", [LOG_DEBUG] = "DEBUG", [LOG_TRACE] = "TRACE", +}; + +void logger_init(logger_output outputCallback) +{ + if (NULL == userCallback) { + userCallback = outputCallback; + } else { + LOGGER_WARN("The logger has already been initialized"); + } + return; +} + +void logger_sendMsg(logger_logLevel logLevel, const char *format, ...) +{ + if (NULL == userCallback || logLevel <= LOG_OFF) { + return; + } + + char buff[BUFFERSIZE]; + + // Add log level + snprintf(buff, BUFFERSIZE, "%s: ", logLevelMsg[logLevel]); + + uint8_t preMsgLen = strlen(buff); + + va_list args; + va_start(args, format); + vsnprintf(buff + preMsgLen, BUFFERSIZE - preMsgLen, format, args); + va_end(args); + + userCallback(buff); + return; +} +\ No newline at end of file diff --git a/xotp_app/utils/src/ringbuffer.c b/xotp_app/utils/src/ringbuffer.c @@ -0,0 +1,145 @@ +/** + * @file ringbuffer.c + * @author Adrian STEINER (steia19@bfh.ch) + * @brief Base ringbuffer header file for handling an unspecific data in a ring buffer design + * @version 0.2 + * @date 2024-03-15 + * @date 2025-02-13 + * + * @copyright (C) 2025 Adrian STEINER + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https: //www.gnu.org/licenses/>. + * + */ + +#include "ringbuffer.h" +#include <stddef.h> +#include <string.h> + +ringbuffer_StatusType +ringbuffer_init(ringbuffer_handler *rbHandler, void *buffer, uint32_t length, uint32_t elmSize) { + if (rbHandler == NULL || buffer == NULL || length == 0 || elmSize == 0) { + return RINGBUFFER_ARGUMENT_ERROR; + } + rbHandler->dataBuffer = buffer; + rbHandler->length = length; + rbHandler->elmSize = elmSize; + + // Initialise head and tail positions + rbHandler->headPos = 0; + rbHandler->tailPos = 0; + + return RINGBUFFER_OK; +} + +void *ringbuffer_destroy(ringbuffer_handler *rbHandler) { + if (rbHandler == NULL) { + return NULL; + } + void *bufferArray = rbHandler->dataBuffer; + rbHandler->dataBuffer = NULL; + return bufferArray; +} + +ringbuffer_StatusType ringbuffer_enqueue(ringbuffer_handler *rbHandler, void *data) { + if (rbHandler == NULL || data == NULL) { + return RINGBUFFER_ARGUMENT_ERROR; + } + + if (ringbuffer_isFull(rbHandler)) { + return RINGBUFFER_OVERFLOW; + } + + /* Insert element */ + if ( + NULL == memcpy( + data, + (void *)((uint8_t *)rbHandler->dataBuffer + (rbHandler->tailPos * rbHandler->elmSize)), + rbHandler->elmSize)) { + return RINGBUFFER_ERROR; + } + rbHandler->tailPos = (rbHandler->tailPos + 1) % rbHandler->length; + return RINGBUFFER_OK; +} + +ringbuffer_StatusType ringbuffer_dequeue(ringbuffer_handler *rbHandler, void **data) { + if (rbHandler == NULL || data == NULL) { + return RINGBUFFER_ARGUMENT_ERROR; + } + + if (ringbuffer_isEmpty(rbHandler)) { + return RINGBUFFER_EMPTY; + } + + (*data) = (uint8_t *)rbHandler->dataBuffer + (rbHandler->headPos * rbHandler->elmSize); + + rbHandler->headPos = (rbHandler->headPos + 1) % rbHandler->length; + + return RINGBUFFER_OK; +} + +void *ringbuffer_peek(const ringbuffer_handler *rbHandler) { + if (rbHandler == NULL) { + return NULL; + } + if (ringbuffer_isEmpty(rbHandler)) { + return NULL; + } + return (void *)((uint8_t *)rbHandler->dataBuffer + rbHandler->headPos * rbHandler->elmSize); +} + +uint32_t ringbuffer_size(const ringbuffer_handler *rbHandler) { + if (rbHandler == NULL) { + return 0; + } + // Check if a overflow of array is recognised + if (rbHandler->headPos > rbHandler->tailPos) { + return (rbHandler->length - rbHandler->headPos + rbHandler->tailPos); + } + // No overflow, calculate back - front + else { + return rbHandler->headPos - rbHandler->tailPos; + } +} + +bool ringbuffer_isFull(const ringbuffer_handler *rbHandler) { + if (rbHandler == NULL) { + return false; + } + + if ((rbHandler->tailPos + 1) % rbHandler->length == rbHandler->headPos) { + return true; + } else { + return false; + } +} + +bool ringbuffer_isEmpty(const ringbuffer_handler *rbHandler) { + if (rbHandler == NULL) { + return false; + } + + if (rbHandler->tailPos == rbHandler->headPos) { + return true; + } else { + return false; + } +} + +ringbuffer_StatusType ringbuffer_clear(ringbuffer_handler *rbHandler) { + if (rbHandler == NULL) { + return RINGBUFFER_ARGUMENT_ERROR; + } + rbHandler->tailPos = rbHandler->headPos; + return RINGBUFFER_OK; +} +\ No newline at end of file diff --git a/xotp_app/xtotp_app.make b/xotp_app/xtotp_app.make @@ -0,0 +1,54 @@ +# Makefile base for xtotp-base-firmware +# Include this file with +# base-firmware relative path to base project +XTOTP_BASE_SOURCE_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) +# Logger options +LOGGER_LEVEL ?= 0 +# Current time +CURRENT_TIME := $(shell date +'%s') +# Version.h file in this directory to be fix included and not versioned +VERSION_H := $(XTOTP_BASE_SOURCE_DIR)/version.h + +XTOTP_C_ARGS = -DLOGGER_LEVEL=$(LOGGER_LEVEL) +XTOTP_LDFLAGS = + +# Module paths +LIB_ORIG_DIR := ../third_party/ + +PBM_LIB_DIR := $(XTOTP_BASE_SOURCE_DIR)/$(LIB_ORIG_DIR)/pbmLib +TINYTIME_LIB_DIR := $(XTOTP_BASE_SOURCE_DIR)/$(LIB_ORIG_DIR)/tinytime +BASEX_LIB_DIR := $(XTOTP_BASE_SOURCE_DIR)/$(LIB_ORIG_DIR)/baseX-Converter + +XTOTP_C_SOURCES = \ + $(wildcard $(XTOTP_BASE_SOURCE_DIR)/app/src/*.c) \ + $(wildcard $(XTOTP_BASE_SOURCE_DIR)/interfaces/src/*.c) \ + $(wildcard $(XTOTP_BASE_SOURCE_DIR)/utils/src/*.c) \ + $(PBM_LIB_DIR)/src/pbm_graphics.c \ + $(wildcard $(TINYTIME_LIB_DIR)/src/*.c) \ + $(wildcard $(BASEX_LIB_DIR)/src/*.c) + +XTOTP_C_INC = \ + -I$(XTOTP_BASE_SOURCE_DIR)/app/inc \ + -I$(XTOTP_BASE_SOURCE_DIR)/interfaces/inc \ + -I$(XTOTP_BASE_SOURCE_DIR)/utils/inc \ + -I$(PBM_LIB_DIR)/inc \ + -I$(TINYTIME_LIB_DIR)/inc \ + -I$(BASEX_LIB_DIR)/inc \ + -I$(XTOTP_BASE_SOURCE_DIR) + +.PHONY: all FORCE + +all: $(VERSION_H) + +$(VERSION_H): FORCE + @$(Q)echo "-- -- -- -- -- -- -- -- -- -- --" + @$(Q)echo "-- Firmware Version Extractor --" + @$(Q)echo "-- -- -- -- -- -- -- -- -- -- --" + @$(Q)echo "#pragma once" > $@ + @$(Q)echo "#define FIRMWARE_VERSION \"$(shell git describe --always --dirty --tags)\"" >> $@ + @$(Q)git log --pretty=format:'#define FIRMWARE_DATE "%cs"' -n 1 >> $@ + @$(Q)echo "\n#define FIRMWARE_COMPILE_TIME $(CURRENT_TIME)" >> $@ + @$(Q)echo "\n Version symboles defined and\n placed in \"$@\" file." + +.PHONY: FORCE +FORCE: