taler-xotp_fw

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

xtotpStateMachine.c (25928B)


      1 /**
      2  * @file xtotpStateMachine.c
      3  * @author Adrian STEINER (steia19@bfh.ch)
      4  * @brief State machine of the xtotp device
      5  * @version 0.1
      6  * @date 17-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 "xtotpStateMachine.h"
     25 #include "logger.h"
     26 
     27 #include <stdio.h>
     28 #include <string.h>
     29 
     30 #include "frontend.h"
     31 #include "talerAmount.h"
     32 #include "xtotpConfig.h"
     33 #include "xtotpCryptoHandler.h"
     34 #include "xtotpRegisterSecret.h"
     35 #include "xtotpSettings.h"
     36 #include "xtotpUtil.h"
     37 
     38 #define ARROW_RIGHT_SYMBOL (26) ///< Symbol for right arrow
     39 #define ARROW_LEFT_SYMBOL (27)  ///< Symbol for left arrow
     40 
     41 void sleepEntry(xtotp_Data *appData,
     42                 inputLevelState inputState,
     43                 inputButtonPos inputButton);
     44 void sleep(xtotp_Data *appData,
     45            inputLevelState inputState,
     46            inputButtonPos inputButton);
     47 
     48 void startupEntry(xtotp_Data *appData,
     49                   inputLevelState inputState,
     50                   inputButtonPos inputButton);
     51 void startup(xtotp_Data *appData,
     52              inputLevelState inputState,
     53              inputButtonPos inputButton);
     54 
     55 void dispatcherEntry(xtotp_Data *appData,
     56                      inputLevelState inputState,
     57                      inputButtonPos inputButton);
     58 void dispatcher(xtotp_Data *appData,
     59                 inputLevelState inputState,
     60                 inputButtonPos inputButton);
     61 
     62 void enterAmountEntry(xtotp_Data *appData,
     63                       inputLevelState inputState,
     64                       inputButtonPos inputButton);
     65 void enterAmount(xtotp_Data *appData,
     66                  inputLevelState inputState,
     67                  inputButtonPos inputButton);
     68 
     69 void autoPayStartEntry(xtotp_Data *appData,
     70                        inputLevelState inputState,
     71                        inputButtonPos inputButton);
     72 void autoPayStart(xtotp_Data *appData,
     73                   inputLevelState inputState,
     74                   inputButtonPos inputButton);
     75 
     76 void autoPayWaitResposeEntry(xtotp_Data *appData,
     77                              inputLevelState inputState,
     78                              inputButtonPos inputButton);
     79 void autoPayWaitRespose(xtotp_Data *appData,
     80                         inputLevelState inputState,
     81                         inputButtonPos inputButton);
     82 
     83 void autoPayVerifyEntry(xtotp_Data *appData,
     84                         inputLevelState inputState,
     85                         inputButtonPos inputButton);
     86 void autoPayVerify(xtotp_Data *appData,
     87                    inputLevelState inputState,
     88                    inputButtonPos inputButton);
     89 
     90 void showXTOTPEntry(xtotp_Data *appData,
     91                     inputLevelState inputState,
     92                     inputButtonPos inputButton);
     93 void showXTOTP(xtotp_Data *appData,
     94                inputLevelState inputState,
     95                inputButtonPos inputButton);
     96 
     97 void showTOTPEntry(xtotp_Data *appData,
     98                    inputLevelState inputState,
     99                    inputButtonPos inputButton);
    100 void showTOTP(xtotp_Data *appData,
    101               inputLevelState inputState,
    102               inputButtonPos inputButton);
    103 
    104 void goToSettingsEntry(xtotp_Data *appData,
    105                        inputLevelState inputState,
    106                        inputButtonPos inputButton);
    107 void goToSettings(xtotp_Data *appData,
    108                   inputLevelState inputState,
    109                   inputButtonPos inputButton);
    110 
    111 void settingsEntry(xtotp_Data *appData,
    112                    inputLevelState inputState,
    113                    inputButtonPos inputButton);
    114 void settings(xtotp_Data *appData,
    115               inputLevelState inputState,
    116               inputButtonPos inputButton);
    117 
    118 stateHandler stateCallbacks[XTOTP_MAX_STATES] = {
    119     [XTOTP_SLEEP] = {.entryAction = sleepEntry, .runAction = sleep},
    120     [XTOTP_STARTUP] = {.entryAction = startupEntry, .runAction = startup},
    121     [XTOTP_DISPATCHER] = {.entryAction = dispatcherEntry,
    122                           .runAction = dispatcher},
    123     [XTOTP_ENTER_AMOUNT] = {.entryAction = enterAmountEntry,
    124                             .runAction = enterAmount},
    125     [XTOTP_AUTOPAY_START] = {.entryAction = autoPayStartEntry,
    126                              .runAction = autoPayStart},
    127     [XTOTP_AUTOPAY_WAIT_RESPONSE] = {.entryAction = autoPayWaitResposeEntry,
    128                                      .runAction = autoPayWaitRespose},
    129     [XTOTP_AUTOPAY_VERIFY] = {.entryAction = autoPayVerifyEntry,
    130                               .runAction = autoPayVerify},
    131     [XTOTP_SHOW_XTOTP] = {.entryAction = showXTOTPEntry,
    132                           .runAction = showXTOTP},
    133     [XTOTP_SHOW_TOTP] = {.entryAction = showTOTPEntry, .runAction = showTOTP},
    134     [XTOTP_GO_TO_SETTINGS] = {.entryAction = goToSettingsEntry,
    135                               .runAction = goToSettings},
    136     [XTOTP_SETTINGS] = {.entryAction = settingsEntry, .runAction = settings},
    137 };
    138 
    139 void xtotp_processStateMachine(xtotp_Data *appData,
    140                                inputLevelState inputState,
    141                                inputButtonPos inputButton)
    142 {
    143   xtotp_BaseStates entryState = appData->currentStates.base;
    144   stateCallbacks[entryState].runAction(appData, inputState, inputButton);
    145   while (entryState != appData->currentStates.base) {
    146     entryState = appData->currentStates.base;
    147     stateCallbacks[entryState].entryAction(appData, inputState, inputButton);
    148   }
    149   appData->processData.updateDisplayData = false;
    150 }
    151 
    152 void sleepEntry(xtotp_Data *appData,
    153                 inputLevelState inputState,
    154                 inputButtonPos inputButton)
    155 {
    156   (void)inputState;
    157   (void)inputButton;
    158   frontend_clear(appData);
    159   appData->iDisplay.show();
    160   return; // Do nothing
    161 }
    162 
    163 void sleep(xtotp_Data *appData,
    164            inputLevelState inputState,
    165            inputButtonPos inputButton)
    166 {
    167   (void)inputState;
    168   (void)inputButton;
    169 
    170   appData->iDisplay.powerOff();
    171   appData->iInput.powerOff();
    172   appData->iClock.powerOff();
    173   appData->iSync.powerOff();
    174   appData->iBatMeas.powerOff();
    175   /* Shutdown device */
    176   appData->iDevice.shutdown();
    177   appData->currentStates.base = XTOTP_STARTUP;
    178 }
    179 
    180 void startupEntry(xtotp_Data *appData,
    181                   inputLevelState inputState,
    182                   inputButtonPos inputButton)
    183 {
    184   (void)inputState;
    185   (void)inputButton;
    186   appData->iDisplay.powerOn();
    187   appData->iInput.powerOn();
    188   return;
    189 }
    190 void startup(xtotp_Data *appData,
    191              inputLevelState inputState,
    192              inputButtonPos inputButton)
    193 {
    194   // Unused parameters
    195   (void)inputState;
    196   (void)inputButton;
    197   // Turn on display and input
    198   appData->iDisplay.powerOn();
    199   appData->iInput.powerOn();
    200   appData->currentStates.base = XTOTP_DISPATCHER;
    201   return;
    202 }
    203 
    204 void dispatcherEntry(xtotp_Data *appData,
    205                      inputLevelState inputState,
    206                      inputButtonPos inputButton)
    207 {
    208   (void)inputState;
    209   (void)inputButton;
    210   xtotpAlgoSettingsType *usedSettings =
    211       crypto_getCurrentAlgorithm(&appData->cryptoHandler);
    212   switch (crypto_getAlgoInfo(usedSettings)) {
    213   case ALGO_XTOTP:
    214     appData->currentStates.base = XTOTP_ENTER_AMOUNT;
    215     return;
    216   case ALGO_TOTP:
    217     appData->currentStates.base = XTOTP_SHOW_TOTP;
    218     return;
    219   case ALGO_UNINITIALIZED:
    220   default:
    221     break;
    222   }
    223   frontend_createBaseWindow(appData, "KEY SELECT", "NO SECRET");
    224   return;
    225 }
    226 
    227 void dispatcher(xtotp_Data *appData,
    228                 inputLevelState inputState,
    229                 inputButtonPos inputButton)
    230 {
    231   (void)inputButton;
    232   if (INPUT_STATE_RISING_EDGE == inputState) {
    233     appData->currentStates.base = XTOTP_SETTINGS;
    234   }
    235   return;
    236 }
    237 
    238 void enterAmountEntry(xtotp_Data *appData,
    239                       inputLevelState inputState,
    240                       inputButtonPos inputButton)
    241 {
    242   (void)inputState;
    243   (void)inputButton;
    244   // Set amount to 0
    245   appData->processData.enteredAmount = 0;
    246   frontend_createBaseWindow(appData, "Enter amount", NULL);
    247   xtotpAlgoSettingsType *usedSettings =
    248       crypto_getCurrentAlgorithm(&appData->cryptoHandler);
    249   if (ALGO_XTOTP == crypto_getAlgoInfo(usedSettings)) {
    250     if (usedSettings->xTalerData.fraction > 0) {
    251       frontend_updateDatafield(
    252           appData, FRONTEND_DATAFIELD_CENTRED, "%*u.%0*u",
    253           MAX_AMOUNT_DIGITS - base10Exponent(usedSettings->xTalerData.fraction),
    254           (uint32_t)appData->processData.enteredAmount /
    255               usedSettings->xTalerData.fraction,
    256           base10Exponent(usedSettings->xTalerData.fraction),
    257           (uint32_t)appData->processData.enteredAmount %
    258               usedSettings->xTalerData.fraction);
    259     } else {
    260       frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%*u",
    261                                MAX_AMOUNT_DIGITS,
    262                                (uint32_t)appData->processData.enteredAmount);
    263     }
    264     appData->iDisplay.show();
    265   } else {
    266     appData->currentStates.base = XTOTP_SHOW_XTOTP;
    267   }
    268   return;
    269 }
    270 
    271 void enterAmount(xtotp_Data *appData,
    272                  inputLevelState inputState,
    273                  inputButtonPos inputButton)
    274 {
    275   if (INPUT_STATE_RISING_EDGE == inputState) {
    276     if (inputButton >= INPUT_NONE) {
    277       return;
    278     }
    279     if (0 == appData->processData.enteredAmount && INPUT_0 == inputButton) {
    280       appData->currentStates.base = XTOTP_GO_TO_SETTINGS;
    281       return;
    282     }
    283     uint64_t oldAmount = appData->processData.enteredAmount;
    284     if (input_readNumber(&appData->processData.enteredAmount, MAX_INPUT_LENGTH,
    285                          inputButton)) {
    286       appData->currentStates.base = XTOTP_AUTOPAY_START;
    287       return;
    288     }
    289     if (oldAmount != appData->processData.enteredAmount) {
    290       xtotpAlgoSettingsType *usedSettings =
    291           crypto_getCurrentAlgorithm(&appData->cryptoHandler);
    292 
    293       if (0 == usedSettings->xTalerData.fraction) {
    294         frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "%*u",
    295                                  MAX_AMOUNT_DIGITS,
    296                                  (uint32_t)appData->processData.enteredAmount);
    297       } else {
    298         uint8_t amountValueOffset =
    299             (uint8_t)MAX_AMOUNT_DIGITS -
    300             base10Exponent(usedSettings->xTalerData.fraction);
    301         uint32_t amountValue = (uint32_t)appData->processData.enteredAmount /
    302                                usedSettings->xTalerData.fraction;
    303         uint8_t fractionLength =
    304             base10Exponent(usedSettings->xTalerData.fraction);
    305         uint32_t fraction = (uint32_t)appData->processData.enteredAmount %
    306                             usedSettings->xTalerData.fraction;
    307         LOGGER_DEBUG("Current amount: %*u.%0*u", amountValueOffset, amountValue,
    308                      fractionLength, fraction);
    309         frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    310                                  "%*u.%0*u", amountValueOffset, amountValue,
    311                                  fractionLength, fraction);
    312       }
    313       appData->iDisplay.show();
    314     }
    315   }
    316   return;
    317 }
    318 
    319 void autoPayStartEntry(xtotp_Data *appData,
    320                        inputLevelState inputState,
    321                        inputButtonPos inputButton)
    322 {
    323   (void)inputState;
    324   (void)inputButton;
    325   xtotpAlgoSettingsType *usedAlg =
    326       crypto_getCurrentAlgorithm(&appData->cryptoHandler);
    327   if (strlen((const char *)usedAlg->xTalerData.merchantTemplateName) == 0) {
    328     // No merchant template set, do not use autopay
    329     appData->currentStates.base = XTOTP_SHOW_XTOTP;
    330     return;
    331   }
    332   frontend_createBaseWindow(appData, "Autopay", NULL);
    333   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "ID:%16s",
    334                            appData->cryptoHandler.deviceID);
    335   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "T:%17s",
    336                            usedAlg->xTalerData.merchantTemplateName);
    337   appData->iDisplay.show();
    338   // Send informations:
    339   appData->iAutoPay.powerOn();
    340   if (usedAlg->xTalerData.fraction > 0) {
    341     appData->iAutoPay.transmitState = appData->iAutoPay.sendURL(
    342         "%s/%s?amount=%s:%u.%0*u", appData->cryptoHandler.merchandBackend,
    343         usedAlg->xTalerData.merchantTemplateName, usedAlg->xTalerData.currency,
    344         (uint32_t)appData->processData.enteredAmount /
    345             usedAlg->xTalerData.fraction,
    346         base10Exponent(usedAlg->xTalerData.fraction),
    347         appData->processData.enteredAmount % usedAlg->xTalerData.fraction);
    348   } else {
    349     appData->iAutoPay.transmitState = appData->iAutoPay.sendURL(
    350         "%s/%s?amount=%s:%u", appData->cryptoHandler.merchandBackend,
    351         usedAlg->xTalerData.merchantTemplateName, usedAlg->xTalerData.currency,
    352         (uint32_t)appData->processData.enteredAmount);
    353   }
    354   if (EXIT_SUCCESS == appData->iAutoPay.transmitState) {
    355     appData->iAutoPay.powerOff();
    356   }
    357   return;
    358 }
    359 void autoPayStart(xtotp_Data *appData,
    360                   inputLevelState inputState,
    361                   inputButtonPos inputButton)
    362 {
    363   if (EXIT_SUCCESS != appData->iAutoPay.transmitState) {
    364     autoPayStartEntry(appData, inputState, inputButton);
    365   }
    366   if (XTOTP_REACTION_EDGE == inputState && INPUT_ENTER == inputButton) {
    367     appData->currentStates.base = XTOTP_SHOW_XTOTP;
    368     return;
    369   }
    370   if (appData->iAutoPay.isURLRead()) {
    371     appData->currentStates.base = XTOTP_AUTOPAY_WAIT_RESPONSE;
    372     return;
    373   }
    374   return;
    375 }
    376 
    377 void autoPayWaitResposeEntry(xtotp_Data *appData,
    378                              inputLevelState inputState,
    379                              inputButtonPos inputButton)
    380 {
    381   (void)inputState;
    382   (void)inputButton;
    383   frontend_createBaseWindow(appData, "Autopay", NULL);
    384   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1,
    385                            "Wait for response");
    386   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "Enter to skip");
    387   appData->iDisplay.show();
    388 }
    389 void autoPayWaitRespose(xtotp_Data *appData,
    390                         inputLevelState inputState,
    391                         inputButtonPos inputButton)
    392 {
    393   if (XTOTP_REACTION_EDGE == inputState) {
    394     if (INPUT_ENTER == inputButton) {
    395       appData->currentStates.base = XTOTP_SHOW_XTOTP;
    396       return;
    397     } else if (INPUT_DEL == inputButton) {
    398       appData->currentStates.base = XTOTP_AUTOPAY_START;
    399       return;
    400     }
    401   }
    402   if (appData->iAutoPay.arePasscodesReceived()) {
    403     appData->currentStates.base = XTOTP_AUTOPAY_VERIFY;
    404   }
    405   return;
    406 }
    407 
    408 void autoPayVerifyEntry(xtotp_Data *appData,
    409                         inputLevelState inputState,
    410                         inputButtonPos inputButton)
    411 {
    412   (void)inputState;
    413   (void)inputButton;
    414   uint32_t receivedPasscodes[XTOTP_AUTOPAY_RECEIVED_PASSKEYS];
    415   if (EXIT_FAILURE == appData->iAutoPay.getPasscodes(receivedPasscodes)) {
    416     appData->currentStates.base = XTOTP_AUTOPAY_WAIT_RESPONSE;
    417     return;
    418   }
    419   appData->iClock.powerOn();
    420   uint64_t currentTime = appData->iClock.getUnixTime();
    421   appData->iClock.powerOff();
    422   if (0 == currentTime) {
    423     LOGGER_ERROR("Could not get the current time");
    424     return;
    425   }
    426   appData->processData.lastCode =
    427       crypto_calc_OTP(&appData->cryptoHandler, &appData->iCrypto, currentTime,
    428                       appData->processData.enteredAmount);
    429 
    430   // Check passcodes:
    431   bool verified = false;
    432   for (uint8_t i = 0; i < XTOTP_AUTOPAY_RECEIVED_PASSKEYS; i++) {
    433     if (appData->processData.lastCode == receivedPasscodes[i]) {
    434       verified = true;
    435       break;
    436     }
    437   }
    438 
    439   if (verified) {
    440     frontend_createBaseWindow(appData, "Autopay", "Verified");
    441   } else {
    442     frontend_createBaseWindow(appData, "Autopay", "Invalid!");
    443   }
    444   appData->iDisplay.show();
    445   return;
    446 }
    447 void autoPayVerify(xtotp_Data *appData,
    448                    inputLevelState inputState,
    449                    inputButtonPos inputButton)
    450 {
    451   if (XTOTP_REACTION_EDGE == inputState) {
    452     if (INPUT_ENTER == inputButton) {
    453       appData->currentStates.base = XTOTP_ENTER_AMOUNT;
    454       return;
    455     } else if (INPUT_DEL == inputButton) {
    456       appData->currentStates.base = XTOTP_AUTOPAY_WAIT_RESPONSE;
    457       return;
    458     }
    459   }
    460   return;
    461 }
    462 
    463 void showXTOTPEntry(xtotp_Data *appData,
    464                     inputLevelState inputState,
    465                     inputButtonPos inputButton)
    466 {
    467   (void)inputState;
    468   (void)inputButton;
    469   if (ALGO_XTOTP !=
    470       crypto_getAlgoInfo(crypto_getCurrentAlgorithm(&appData->cryptoHandler))) {
    471     appData->currentStates.base = XTOTP_DISPATCHER;
    472     return;
    473   }
    474   frontend_createBaseWindow(appData, "xTOTP", NULL);
    475   showXTOTP(appData, INPUT_STATE_RISING_EDGE,
    476             INPUT_DEL); // Process to force an update action
    477   return;
    478 }
    479 
    480 void showXTOTP(xtotp_Data *appData,
    481                inputLevelState inputState,
    482                inputButtonPos inputButton)
    483 {
    484   if (INPUT_STATE_RISING_EDGE == inputState) {
    485     switch (inputButton) {
    486     case INPUT_DEL:
    487       appData->iClock.powerOn();
    488       uint64_t currentTime = appData->iClock.getUnixTime();
    489       appData->iClock.powerOff();
    490       if (0 == currentTime) {
    491         LOGGER_ERROR("Could not get the current time");
    492         return;
    493       }
    494 
    495       // Get xTOTP code
    496       appData->processData.lastCode =
    497           crypto_calc_OTP(&appData->cryptoHandler, &appData->iCrypto,
    498                           currentTime, appData->processData.enteredAmount);
    499       xtotpAlgoSettingsType *usedAlg =
    500           crypto_getCurrentAlgorithm(&appData->cryptoHandler);
    501       uint8_t halfCodeLength = usedAlg->digits / 2;
    502       uint32_t decimalSeparator = power10(halfCodeLength);
    503       LOGGER_INFO(
    504           "Code generated %.*u %.*u", halfCodeLength,
    505           (appData->processData.lastCode / decimalSeparator) % decimalSeparator,
    506           halfCodeLength, appData->processData.lastCode % decimalSeparator);
    507       frontend_updateDatafield(
    508           appData, FRONTEND_DATAFIELD_CENTRED, "%.*u %.*u", halfCodeLength,
    509           (appData->processData.lastCode / decimalSeparator) % decimalSeparator,
    510           halfCodeLength, appData->processData.lastCode % decimalSeparator);
    511       appData->iDisplay.show();
    512       break;
    513     case INPUT_ENTER:
    514       appData->currentStates.base = XTOTP_ENTER_AMOUNT;
    515       return;
    516     default:
    517       return;
    518     }
    519   }
    520 }
    521 
    522 void showTOTPEntry(xtotp_Data *appData,
    523                    inputLevelState inputState,
    524                    inputButtonPos inputButton)
    525 {
    526   (void)inputState;
    527   (void)inputButton;
    528   if (ALGO_TOTP !=
    529       crypto_getAlgoInfo(crypto_getCurrentAlgorithm(&appData->cryptoHandler))) {
    530     appData->currentStates.base = XTOTP_DISPATCHER;
    531     return;
    532   }
    533   frontend_createBaseWindow(appData, "TOTP", NULL);
    534   appData->processData.lastNeededTime = 0;
    535   showTOTP(appData, INPUT_STATE_LOW,
    536            INPUT_NONE); // Process to force an update action
    537   return;
    538 }
    539 
    540 void showTOTP(xtotp_Data *appData,
    541               inputLevelState inputState,
    542               inputButtonPos inputButton)
    543 {
    544   if (INPUT_STATE_RISING_EDGE == inputState && INPUT_0 == inputButton) {
    545     appData->currentStates.base = XTOTP_GO_TO_SETTINGS;
    546     return;
    547   }
    548   if (appData->processData.lastNeededTime >= XTOTP_SAMPLE_TIME) {
    549     appData->processData.lastNeededTime -= XTOTP_SAMPLE_TIME;
    550     if (0 == (appData->processData.lastNeededTime % 1000)) {
    551       // Update time
    552       char menuTitleBuffer[16];
    553       snprintf(menuTitleBuffer, sizeof(menuTitleBuffer), "%3d TOTP",
    554                (uint16_t)MS_TO_SECOND(appData->processData.lastNeededTime));
    555       frontend_updateMenuTitle(appData, menuTitleBuffer);
    556       appData->iDisplay.show();
    557     }
    558   } else {
    559     // Update Passcode
    560     appData->iClock.powerOn();
    561     uint64_t currentTime = appData->iClock.getUnixTime();
    562     if (0 == currentTime) {
    563       LOGGER_ERROR("Could not get the current time");
    564       return;
    565     }
    566     LOGGER_DEBUG("Received time: %lu", currentTime);
    567     appData->iClock.powerOff();
    568 
    569     xtotpAlgoSettingsType *usedSecret =
    570         crypto_getCurrentAlgorithm(&appData->cryptoHandler);
    571 
    572     appData->processData.lastCode = crypto_calc_OTP(
    573         &appData->cryptoHandler, &appData->iCrypto, currentTime, 0);
    574     appData->processData.lastNeededTime =
    575         SECOND_TO_MS(usedSecret->interval - currentTime % usedSecret->interval);
    576 
    577     uint8_t halfCodeLength = usedSecret->digits / 2;
    578     uint32_t decimalSeparator = power10(halfCodeLength);
    579     LOGGER_INFO(
    580         "Code generated %.*u %.*u", halfCodeLength,
    581         (appData->processData.lastCode / decimalSeparator) % decimalSeparator,
    582         halfCodeLength, appData->processData.lastCode % decimalSeparator);
    583     frontend_updateDatafield(
    584         appData, FRONTEND_DATAFIELD_CENTRED, "%.*u %.*u", halfCodeLength,
    585         (appData->processData.lastCode / decimalSeparator) % decimalSeparator,
    586         halfCodeLength, appData->processData.lastCode % decimalSeparator);
    587 
    588     char menuTitleBuffer[16];
    589     snprintf(menuTitleBuffer, sizeof(menuTitleBuffer), "%3d TOTP",
    590              (uint16_t)MS_TO_SECOND(appData->processData.lastNeededTime));
    591     frontend_updateMenuTitle(appData, menuTitleBuffer);
    592     appData->iDisplay.show();
    593   }
    594 }
    595 
    596 void goToSettingsEntry(xtotp_Data *appData,
    597                        inputLevelState inputState,
    598                        inputButtonPos inputButton)
    599 {
    600   (void)inputState;
    601   (void)inputButton;
    602   frontend_createBaseWindow(appData, "Settings?", NULL);
    603   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "%c Return",
    604                            ARROW_LEFT_SYMBOL);
    605   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    606                            "Enter %c Settings", ARROW_RIGHT_SYMBOL);
    607   appData->iDisplay.show();
    608   return;
    609 }
    610 void goToSettings(xtotp_Data *appData,
    611                   inputLevelState inputState,
    612                   inputButtonPos inputButton)
    613 {
    614   if (INPUT_STATE_RISING_EDGE == inputState) {
    615     switch (inputButton) {
    616     case INPUT_DEL:
    617       appData->currentStates.base = XTOTP_DISPATCHER;
    618       break;
    619     case INPUT_ENTER:
    620       appData->currentStates.base = XTOTP_SETTINGS;
    621       break;
    622     default:
    623       break;
    624     }
    625   }
    626   return;
    627 }
    628 
    629 void settingsEntry(xtotp_Data *appData,
    630                    inputLevelState inputState,
    631                    inputButtonPos inputButton)
    632 {
    633   (void)inputState;
    634   (void)inputButton;
    635   appData->currentStates.settings = SETTINGS_ENTRY;
    636   appData->currentStates.additionalSettings = ADDITIONAL_MAX_STATES;
    637   appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_MAX_STATES;
    638   frontend_createBaseWindow(appData, "Settings", NULL);
    639   xtotpAlgoSettingsType *selectedAlg =
    640       &appData->cryptoHandler.algorithms[appData->cryptoHandler.usedAlgorithm];
    641   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "%3d%c%us%1uD%s",
    642                            appData->cryptoHandler.usedAlgorithm,
    643                            ARROW_RIGHT_SYMBOL, selectedAlg->interval,
    644                            selectedAlg->digits,
    645                            crypto_getAlgoName(selectedAlg));
    646   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "0 %c Additional",
    647                            ARROW_RIGHT_SYMBOL);
    648   appData->iDisplay.show();
    649   return;
    650 }
    651 
    652 void settings(xtotp_Data *appData,
    653               inputLevelState inputState,
    654               inputButtonPos inputButton)
    655 {
    656   (void)inputState;
    657   (void)inputButton;
    658 
    659   xtotp_SettingStates oldSettings = appData->currentStates.settings;
    660 
    661   switch (appData->currentStates.settings) {
    662   case SETTINGS_ENTRY:
    663     if (INPUT_STATE_RISING_EDGE == inputState) {
    664       switch (inputButton) {
    665       case INPUT_DEL:
    666         appData->currentStates.base = XTOTP_DISPATCHER;
    667         break;
    668       case INPUT_ENTER:
    669         appData->currentStates.base = XTOTP_DISPATCHER;
    670         break;
    671       case INPUT_0:
    672         appData->currentStates.settings = SETTINGS_ADDITIONAL;
    673         settings_processAdditional(appData, INPUT_STATE_LOW, INPUT_NONE);
    674         break;
    675       default:
    676         if (inputButton > INPUT_0 && inputButton <= INPUT_9) {
    677           uint64_t readInput = 0;
    678           input_readNumber(&readInput, XTOTP_STORABLE_SECRETS, inputButton);
    679           appData->cryptoHandler.processedAlgorithm = (uint8_t)readInput;
    680           frontend_showSecretState(appData);
    681           appData->iDisplay.show();
    682           appData->currentStates.settings = SETTINGS_SELECT_KEY;
    683         }
    684         break;
    685       }
    686     }
    687     break;
    688   case SETTINGS_SELECT_KEY:
    689     if (INPUT_STATE_RISING_EDGE == inputState) {
    690       uint64_t readInput = (uint64_t)appData->cryptoHandler.processedAlgorithm;
    691       if (input_readNumber((uint64_t *)&readInput, XTOTP_STORABLE_SECRETS,
    692                            inputButton)) {
    693         if (appData->cryptoHandler.processedAlgorithm > 0 &&
    694             appData->cryptoHandler.processedAlgorithm <=
    695                 XTOTP_STORABLE_SECRETS) {
    696           appData->currentStates.settings = SETTINGS_REGISTER_KEY;
    697           settings_processSecretHandling(appData, INPUT_STATE_LOW, INPUT_NONE);
    698           break;
    699         } else {
    700           appData->cryptoHandler.processedAlgorithm = 0;
    701         }
    702       }
    703       appData->cryptoHandler.processedAlgorithm = (uint8_t)readInput;
    704       if (INPUT_DEL == inputButton && 0 == readInput) {
    705         appData->currentStates.settings = SETTINGS_ENTRY;
    706         break;
    707       }
    708       frontend_showSecretState(appData);
    709       appData->iDisplay.show();
    710     }
    711     break;
    712   case SETTINGS_REGISTER_KEY:
    713     settings_processSecretHandling(appData, inputState, inputButton);
    714     break;
    715   case SETTINGS_ADDITIONAL:
    716     settings_processAdditional(appData, inputState, inputButton);
    717     break;
    718   default:
    719     appData->currentStates.settings = SETTINGS_ENTRY;
    720     return;
    721   }
    722   if (oldSettings != appData->currentStates.settings &&
    723       SETTINGS_ENTRY == appData->currentStates.settings) {
    724     settingsEntry(appData, inputState, inputButton);
    725   }
    726 }