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:
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, ¤cyLength,
+ 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: