taler-xotp_fw

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

xtotpSettings.c (21260B)


      1 /**
      2  * @file xtotpSettings.c
      3  * @author Adrian STEINER (steia19@bfh.ch)
      4  * @brief Settings state machine for secret registration and additional settings
      5  * @version 0.1
      6  * @date 27-02-2025
      7  *
      8  * @copyright (C) 2025 Adrian STEINER
      9  * This program is free software: you can redistribute it and/or modify
     10  * it under the terms of the GNU General Public License as published by
     11  * the Free Software Foundation, either version 3 of the License, or
     12  * (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program.  If not, see <https: //www.gnu.org/licenses/>.
     21  *
     22  */
     23 
     24 #include "xtotpSettings.h"
     25 
     26 // #include "version.h"
     27 
     28 #include <stdio.h>
     29 #include <string.h>
     30 
     31 #include "base32_converter.h"
     32 #include "base8_converter.h"
     33 
     34 #include "frontend.h"
     35 #include "inputInterface.h"
     36 #include "logger.h"
     37 #include "tinytime.h"
     38 #include "xtotpConfig.h"
     39 #include "xtotpStateMachine.h"
     40 #include "xtotpUtil.h"
     41 
     42 /**
     43  * @brief This function sets for the additional settings the next state with the
     44  * given input.
     45  *
     46  * This function needs to be called if no special settings is enabled (like
     47  * updating a value).
     48  * @note To be free to use this function, call it in the state as you like
     49  * (rising/falling edge/ high).
     50  *
     51  * @param appData The user data handler
     52  * @param inputButton The current input button
     53  */
     54 void checkBaseAdditionalInput(xtotp_Data *appData, inputButtonPos inputButton);
     55 
     56 /* Additional state callback functions */
     57 void additionalClockEntry(xtotp_Data *appData,
     58                           inputLevelState inputState,
     59                           inputButtonPos inputButton);
     60 void additionalClock(xtotp_Data *appData,
     61                      inputLevelState inputState,
     62                      inputButtonPos inputButton);
     63 
     64 void additionalBatteryStateEntry(xtotp_Data *appData,
     65                                  inputLevelState inputState,
     66                                  inputButtonPos inputButton);
     67 void additionalBatteryState(xtotp_Data *appData,
     68                             inputLevelState inputState,
     69                             inputButtonPos inputButton);
     70 
     71 void additionalLastSyncTimeEntry(xtotp_Data *appData,
     72                                  inputLevelState inputState,
     73                                  inputButtonPos inputButton);
     74 void additionalLastSyncTime(xtotp_Data *appData,
     75                             inputLevelState inputState,
     76                             inputButtonPos inputButton);
     77 
     78 void additionalDifferenceSyncEntry(xtotp_Data *appData,
     79                                    inputLevelState inputState,
     80                                    inputButtonPos inputButton);
     81 void additionalDifferenceSync(xtotp_Data *appData,
     82                               inputLevelState inputState,
     83                               inputButtonPos inputButton);
     84 
     85 void additionalTimeoutTimeEntry(xtotp_Data *appData,
     86                                 inputLevelState inputState,
     87                                 inputButtonPos inputButton);
     88 void additionalTimeoutTime(xtotp_Data *appData,
     89                            inputLevelState inputState,
     90                            inputButtonPos inputButton);
     91 
     92 void additionalScreenModeEntry(xtotp_Data *appData,
     93                                inputLevelState inputState,
     94                                inputButtonPos inputButton);
     95 void additionalScreenMode(xtotp_Data *appData,
     96                           inputLevelState inputState,
     97                           inputButtonPos inputButton);
     98 
     99 void additionalDeviceIDEntry(xtotp_Data *appData,
    100                              inputLevelState inputState,
    101                              inputButtonPos inputButton);
    102 void additionalDeviceID(xtotp_Data *appData,
    103                         inputLevelState inputState,
    104                         inputButtonPos inputButton);
    105 
    106 void additionalFirmwareEntry(xtotp_Data *appData,
    107                              inputLevelState inputState,
    108                              inputButtonPos inputButton);
    109 void additionalFirmware(xtotp_Data *appData,
    110                         inputLevelState inputState,
    111                         inputButtonPos inputButton);
    112 
    113 void additionalUnusedEntry(xtotp_Data *appData,
    114                            inputLevelState inputState,
    115                            inputButtonPos inputButton);
    116 void additionalUnused(xtotp_Data *appData,
    117                       inputLevelState inputState,
    118                       inputButtonPos inputButton);
    119 
    120 stateHandler additionalSettingsState[ADDITIONAL_MAX_STATES] = {
    121     [ADDITIONAL_CLOCK] = {.entryAction = additionalClockEntry,
    122                           .runAction = additionalClock},
    123     [ADDITIONAL_BATTERY_STATE] = {.entryAction = additionalBatteryStateEntry,
    124                                   .runAction = additionalBatteryState},
    125     [ADDITIONAL_LAST_SYNC_TIME] = {.entryAction = additionalLastSyncTimeEntry,
    126                                    .runAction = additionalLastSyncTime},
    127     [ADDITIONAL_DIFFERENCE_SYNC] = {.entryAction =
    128                                         additionalDifferenceSyncEntry,
    129                                     .runAction = additionalDifferenceSync},
    130     [ADDITIONAL_TIMEOUT_TIME] = {.entryAction = additionalTimeoutTimeEntry,
    131                                  .runAction = additionalTimeoutTime},
    132     [ADDITIONAL_SCREEN_MODE] = {.entryAction = additionalScreenModeEntry,
    133                                 .runAction = additionalScreenMode},
    134     [ADDITIONAL_DEVICE_ID] = {.entryAction = additionalDeviceIDEntry,
    135                               .runAction = additionalDeviceID},
    136     [ADDITIONAL_UNUSED_7] = {.entryAction = additionalUnusedEntry,
    137                              .runAction = additionalUnused},
    138     [ADDITIONAL_UNSUED_8] = {.entryAction = additionalUnusedEntry,
    139                              .runAction = additionalUnused},
    140     [ADDITIONAL_FIRMWARE] = {.entryAction = additionalFirmwareEntry,
    141                              .runAction = additionalFirmware}};
    142 
    143 void settings_processAdditional(xtotp_Data *appData,
    144                                 inputLevelState inputState,
    145                                 inputButtonPos inputButton)
    146 {
    147   if (NULL == appData) {
    148     return;
    149   }
    150   xtotp_AdditionalSettingStates oldState =
    151       appData->currentStates.additionalSettings;
    152   if (appData->currentStates.additionalSettings >= ADDITIONAL_MAX_STATES) {
    153     appData->currentStates.additionalSettings = ADDITIONAL_CLOCK;
    154   } else {
    155     additionalSettingsState[appData->currentStates.additionalSettings]
    156         .runAction(appData, inputState, inputButton);
    157   }
    158   if (oldState != appData->currentStates.additionalSettings) {
    159     additionalSettingsState[appData->currentStates.additionalSettings]
    160         .entryAction(appData, inputState, inputButton);
    161   }
    162 }
    163 
    164 void checkBaseAdditionalInput(xtotp_Data *appData, inputButtonPos inputButton)
    165 {
    166   switch (inputButton) {
    167   case INPUT_0:
    168     appData->currentStates.additionalSettings = ADDITIONAL_CLOCK;
    169     return;
    170   case INPUT_1:
    171     appData->currentStates.additionalSettings = ADDITIONAL_BATTERY_STATE;
    172     return;
    173   case INPUT_2:
    174     appData->currentStates.additionalSettings = ADDITIONAL_LAST_SYNC_TIME;
    175     return;
    176   case INPUT_3:
    177     appData->currentStates.additionalSettings = ADDITIONAL_DIFFERENCE_SYNC;
    178     return;
    179   case INPUT_4:
    180     appData->currentStates.additionalSettings = ADDITIONAL_TIMEOUT_TIME;
    181     return;
    182   case INPUT_5:
    183     appData->currentStates.additionalSettings = ADDITIONAL_SCREEN_MODE;
    184     return;
    185   case INPUT_6:
    186     appData->currentStates.additionalSettings = ADDITIONAL_DEVICE_ID;
    187     return;
    188   case INPUT_9:
    189     appData->currentStates.additionalSettings = ADDITIONAL_FIRMWARE;
    190     return;
    191   case INPUT_ENTER:
    192     appData->currentStates.base = XTOTP_DISPATCHER;
    193     return;
    194   case INPUT_DEL:
    195     appData->currentStates.base = XTOTP_DISPATCHER;
    196     return;
    197   default:
    198     // Do nothing
    199     return;
    200   }
    201 }
    202 
    203 void additionalClockEntry(xtotp_Data *appData,
    204                           inputLevelState inputState,
    205                           inputButtonPos inputButton)
    206 {
    207   (void)inputState;
    208   (void)inputButton;
    209   appData->processData.lastNeededTime = 0;
    210   frontend_createBaseWindow(appData, "Time & Date", NULL);
    211   additionalClock(appData, INPUT_STATE_LOW, INPUT_NONE);
    212   return;
    213 }
    214 
    215 void additionalClock(xtotp_Data *appData,
    216                      inputLevelState inputState,
    217                      inputButtonPos inputButton)
    218 {
    219   if (appData->secFlag || 0 == appData->processData.lastNeededTime) {
    220     appData->iClock.powerOn();
    221     uint64_t currentTime = appData->iClock.getUnixTime();
    222     appData->iClock.powerOff();
    223     if (currentTime != appData->processData.lastNeededTime) {
    224       appData->processData.lastNeededTime = currentTime;
    225       tinyTimeType tad;
    226       tiny_getTimeType(&tad, appData->processData.lastNeededTime);
    227       frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1,
    228                                "%.2u:%.2u:%.2u", tad.hour, tad.min, tad.sec);
    229       frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    230                                "%.2u.%.2u.%.4u", tad.monthDay, tad.month,
    231                                tad.year);
    232       appData->iDisplay.show();
    233     }
    234   }
    235   if (inputState == INPUT_STATE_RISING_EDGE) {
    236     checkBaseAdditionalInput(appData, inputButton);
    237   }
    238 }
    239 
    240 void additionalBatteryStateEntry(xtotp_Data *appData,
    241                                  inputLevelState inputState,
    242                                  inputButtonPos inputButton)
    243 {
    244   (void)inputState;
    245   (void)inputButton;
    246   batMeas_measure(&appData->iBatMeas, appData->iDevice.getTick(), APP_SEC_BASE);
    247   frontend_createBaseWindow(appData, "Battery", NULL);
    248   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%u mV",
    249                            batMeas_getBatteryVoltageState(&appData->iBatMeas));
    250   appData->iDisplay.show();
    251   return;
    252 }
    253 void additionalBatteryState(xtotp_Data *appData,
    254                             inputLevelState inputState,
    255                             inputButtonPos inputButton)
    256 {
    257   if (!batMeas_measure(&appData->iBatMeas, appData->iDevice.getTick(),
    258                        APP_SEC_BASE)) {
    259     frontend_writeStatusIcons(appData);
    260     frontend_updateDatafield(
    261         appData, FRONTEND_DATAFIELD_CENTRED, "%u mV",
    262         batMeas_getBatteryVoltageState(&appData->iBatMeas));
    263     appData->iDisplay.show();
    264   }
    265   if (inputState == INPUT_STATE_RISING_EDGE) {
    266     checkBaseAdditionalInput(appData, inputButton);
    267   }
    268 }
    269 
    270 void additionalLastSyncTimeEntry(xtotp_Data *appData,
    271                                  inputLevelState inputState,
    272                                  inputButtonPos inputButton)
    273 {
    274   (void)inputState;
    275   (void)inputButton;
    276   frontend_createBaseWindow(appData, "Last Sync", NULL);
    277   appData->processData.updateDisplayData = true;
    278   additionalLastSyncTime(appData, INPUT_STATE_LOW, INPUT_NONE);
    279 }
    280 
    281 void additionalLastSyncTime(xtotp_Data *appData,
    282                             inputLevelState inputState,
    283                             inputButtonPos inputButton)
    284 {
    285   if (appData->processData.updateDisplayData) {
    286     tinyTimeType tad;
    287     tiny_getTimeType(&tad, appData->iSync.lastSyncTime);
    288     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1,
    289                              "%.2u:%.2u:%.2u %.2u.%.2u.%.4u", tad.hour, tad.min,
    290                              tad.sec, tad.monthDay, tad.month, tad.year);
    291     tiny_getTimeType(&tad, appData->iSync.expectedSyncTime);
    292     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    293                              "%.2u:%.2u:%.2u %.2u.%.2u.%.4u", tad.hour, tad.min,
    294                              tad.sec, tad.monthDay, tad.month, tad.year);
    295     appData->iDisplay.show();
    296   }
    297   // Check window update
    298   if (INPUT_STATE_RISING_EDGE == inputState) {
    299     if (INPUT_DEL == inputButton && !appData->iSync.isSynchronizing) {
    300       sync_enable(&appData->iSync);
    301       frontend_writeStatusIcons(appData);
    302       appData->iDisplay.show();
    303       return;
    304     }
    305     checkBaseAdditionalInput(appData, inputButton);
    306   }
    307 }
    308 
    309 void additionalDifferenceSyncEntry(xtotp_Data *appData,
    310                                    inputLevelState inputState,
    311                                    inputButtonPos inputButton)
    312 {
    313   (void)inputState;
    314   (void)inputButton;
    315   frontend_createBaseWindow(appData, "Sync Info", NULL);
    316   appData->processData.updateDisplayData = true;
    317   additionalDifferenceSync(appData, INPUT_STATE_LOW, inputButton);
    318   return;
    319 }
    320 void additionalDifferenceSync(xtotp_Data *appData,
    321                               inputLevelState inputState,
    322                               inputButtonPos inputButton)
    323 {
    324   // Update at startup/ new data
    325   if (appData->processData.updateDisplayData) {
    326     frontend_writeStatusIcons(appData);
    327     uint64_t syncHour = 0;
    328     uint64_t syncMin = 0;
    329     uint64_t syncSec = tiny_convertSeconds(
    330         MS_TO_SECOND(appData->iSync.syncDiff), NULL, &syncHour, &syncMin);
    331     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1,
    332                              "Dif:%7s%.2u:%.2u:%.2u", " ", (uint32_t)syncHour,
    333                              (uint32_t)syncMin, (uint32_t)syncSec);
    334     if (appData->iSync.syncDuration >= XTOTP_SYNC_MAX_SYNC_TIME) {
    335       frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    336                                "Dur:     Sync error");
    337     } else {
    338       syncSec = tiny_convertSeconds(MS_TO_SECOND(appData->iSync.syncDuration),
    339                                     NULL, &syncHour, &syncMin);
    340       frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    341                                "Dur:%7s%.2u:%.2u:%.2u", " ", (uint32_t)syncHour,
    342                                (uint32_t)syncMin, (uint32_t)syncSec);
    343     }
    344     appData->iDisplay.show();
    345   }
    346   // Sync time
    347   else if (appData->iSync.isSynchronizing && appData->secFlag) {
    348     uint64_t syncHour = 0;
    349     uint64_t syncMin = 0;
    350     uint64_t syncSec = tiny_convertSeconds(
    351         MS_TO_SECOND(appData->iSync.syncDuration), NULL, &syncHour, &syncMin);
    352     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    353                              "Dur:%7s%.2u:%.2u:%.2u", " ", (uint32_t)syncHour,
    354                              (uint32_t)syncMin, (uint32_t)syncSec);
    355     appData->iDisplay.show();
    356   }
    357   // Check window update
    358   if (INPUT_STATE_RISING_EDGE == inputState) {
    359     if (INPUT_DEL == inputButton && !appData->iSync.isSynchronizing) {
    360       sync_enable(&appData->iSync);
    361       frontend_writeStatusIcons(appData);
    362       frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    363                                "Dur:%7s%.2u:%.2u:%.2u", " ", 0, 0, 0);
    364       appData->iDisplay.show();
    365       return;
    366     }
    367     checkBaseAdditionalInput(appData, inputButton);
    368   }
    369 }
    370 
    371 void additionalTimeoutTimeEntry(xtotp_Data *appData,
    372                                 inputLevelState inputState,
    373                                 inputButtonPos inputButton)
    374 {
    375   (void)inputState;
    376   (void)inputButton;
    377   frontend_createBaseWindow(appData, "Sleep Timeout", NULL);
    378   if (appData->timeOutTime != 0) {
    379     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%3u s",
    380                              MS_TO_SECOND(appData->timeOutTime));
    381   } else {
    382     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Disabled");
    383   }
    384   appData->iDisplay.show();
    385   return;
    386 }
    387 bool setNewTime = false;
    388 uint64_t newTimeOut = 0;
    389 
    390 void additionalTimeoutTime(xtotp_Data *appData,
    391                            inputLevelState inputState,
    392                            inputButtonPos inputButton)
    393 {
    394 
    395   if (inputState == XTOTP_REACTION_EDGE) {
    396     if (INPUT_DEL == inputButton && !setNewTime) {
    397       newTimeOut = 0;
    398       setNewTime = true;
    399     }
    400     if (setNewTime) {
    401       if (input_readNumber(&newTimeOut, XTOTP_MAX_TIMEOUT_TIME, inputButton) ||
    402           (0 == newTimeOut && INPUT_ENTER == inputButton)) {
    403         appData->timeOutTime = (uint32_t)SECOND_TO_MS(newTimeOut);
    404         if (0 != newTimeOut) {
    405           frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    406                                    "     %3u s",
    407                                    MS_TO_SECOND(appData->timeOutTime));
    408         } else {
    409           frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    410                                    "Disabled");
    411         }
    412         appData->iDisplay.show();
    413         setNewTime = false;
    414         newTimeOut = 0;
    415       } else {
    416         frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    417                                  "New: %3u s", (uint32_t)newTimeOut);
    418         appData->iDisplay.show();
    419       }
    420     } else {
    421       checkBaseAdditionalInput(appData, inputButton);
    422     }
    423   }
    424 }
    425 
    426 void additionalScreenModeEntry(xtotp_Data *appData,
    427                                inputLevelState inputState,
    428                                inputButtonPos inputButton)
    429 {
    430   (void)inputState;
    431   (void)inputButton;
    432   frontend_createBaseWindow(appData, "Theme",
    433                             (appData->iDisplay.theme == PBM_BLACK) ? "Light"
    434                                                                    : "Dark");
    435   appData->iDisplay.show();
    436   return;
    437 }
    438 void additionalScreenMode(xtotp_Data *appData,
    439                           inputLevelState inputState,
    440                           inputButtonPos inputButton)
    441 {
    442   if (inputState == INPUT_STATE_RISING_EDGE) {
    443     if (inputButton == INPUT_DEL) {
    444       appData->iDisplay.theme =
    445           (appData->iDisplay.theme == PBM_BLACK) ? PBM_WHITE : PBM_BLACK;
    446       additionalScreenModeEntry(appData, inputState, inputButton);
    447     } else {
    448       checkBaseAdditionalInput(appData, inputButton);
    449     }
    450   }
    451 }
    452 
    453 static bool setNewDeviceID = false;
    454 
    455 void additionalDeviceIDEntry(xtotp_Data *appData,
    456                              inputLevelState inputState,
    457                              inputButtonPos inputButton)
    458 {
    459   (void)appData;
    460   (void)inputState;
    461   setNewDeviceID = false;
    462   frontend_createBaseWindow(appData,
    463                             "Device ID:", appData->cryptoHandler.deviceID);
    464   input_readBase8(inputButton, NULL, NULL, (XTOTP_DEVICE_ID_SIZE - 1), true);
    465   appData->iDisplay.show();
    466   return;
    467 }
    468 
    469 void additionalDeviceID(xtotp_Data *appData,
    470                         inputLevelState inputState,
    471                         inputButtonPos inputButton)
    472 {
    473   if (INPUT_STATE_RISING_EDGE == inputState) {
    474     if (INPUT_DEL == inputButton && !setNewDeviceID) {
    475       setNewDeviceID = true;
    476     }
    477     if (setNewDeviceID) {
    478       uint8_t *inputString = NULL;
    479       uint8_t inputLength = 0;
    480       if (input_readBase8(inputButton, &inputString, &inputLength,
    481                           ((XTOTP_DEVICE_ID_SIZE * 8 + 2) / 3), false)) {
    482         LOGGER_DEBUG("Got input string: %s with length: %u", inputString,
    483                      inputLength);
    484         uint8_t numBuf[(XTOTP_DEVICE_ID_SIZE * 8 / 3)];
    485         base8_stringToNum(numBuf, (const char *)inputString);
    486         uint32_t decodedLength = 0;
    487         if (base8_decodeNum((uint8_t *)appData->cryptoHandler.deviceID,
    488                             &decodedLength, (XTOTP_DEVICE_ID_SIZE - 1), numBuf,
    489                             inputLength) == BASEX_OK) {
    490           appData->cryptoHandler.deviceID[decodedLength] = '\0';
    491           frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    492                                    appData->cryptoHandler.deviceID);
    493           input_readBase8(inputButton, NULL, NULL, (XTOTP_DEVICE_ID_SIZE - 1),
    494                           true);
    495           setNewDeviceID = false;
    496         } else {
    497           frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    498                                    "ERR: PARSE");
    499         }
    500         appData->iDisplay.show();
    501       } else {
    502         frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    503                                  (const char *)inputString);
    504         appData->iDisplay.show();
    505       }
    506     } else {
    507       checkBaseAdditionalInput(appData, inputButton);
    508     }
    509   }
    510 }
    511 
    512 void additionalFirmwareEntry(xtotp_Data *appData,
    513                              inputLevelState inputState,
    514                              inputButtonPos inputButton)
    515 {
    516   (void)inputState;
    517   (void)inputButton;
    518   frontend_createBaseWindow(appData, "Firmware", NULL);
    519   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "VER: %14s",
    520                            FIRMWARE_VERSION);
    521   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "DAT: %14s",
    522                            FIRMWARE_DATE);
    523   appData->iDisplay.show();
    524 }
    525 
    526 void additionalFirmware(xtotp_Data *appData,
    527                         inputLevelState inputState,
    528                         inputButtonPos inputButton)
    529 {
    530   if (inputState == INPUT_STATE_RISING_EDGE) {
    531     checkBaseAdditionalInput(appData, inputButton);
    532   }
    533 }
    534 
    535 void additionalUnusedEntry(xtotp_Data *appData,
    536                            inputLevelState inputState,
    537                            inputButtonPos inputButton)
    538 {
    539   (void)inputState;
    540   (void)inputButton;
    541   appData->currentStates.additionalSettings = ADDITIONAL_CLOCK;
    542 }
    543 void additionalUnused(xtotp_Data *appData,
    544                       inputLevelState inputState,
    545                       inputButtonPos inputButton)
    546 {
    547   (void)inputState;
    548   (void)inputButton;
    549   appData->currentStates.additionalSettings = ADDITIONAL_CLOCK;
    550 }