taler-xotp_fw

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

xtotpRegisterSecret.c (22752B)


      1 /**
      2  * @file xtotpRegisterSecret.c
      3  * @author Adrian STEINER (steia19@bfh.ch)
      4  * @brief Settings state machine to register new secret with a corresponding
      5  * althorihm Changing and selecting is as well intergrated in this module
      6  * @version 0.1
      7  * @date 24-07-2025
      8  *
      9  * @copyright (C) 2025 Adrian STEINER
     10  * This program is free software: you can redistribute it and/or modify
     11  * it under the terms of the GNU General Public License as published by
     12  * the Free Software Foundation, either version 3 of the License, or
     13  * (at your option) any later version.
     14  *
     15  * This program is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  * GNU General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU General Public License
     21  * along with this program.  If not, see <https: //www.gnu.org/licenses/>.
     22  *
     23  */
     24 
     25 #include "xtotpRegisterSecret.h"
     26 #include "base32_converter.h"
     27 #include "base8_converter.h"
     28 #include "frontend.h"
     29 #include "xtotpStateMachine.h"
     30 #include <string.h>
     31 
     32 static uint64_t usedInputNumber = 0;
     33 
     34 void registerDispatcherEntry(xtotp_Data *appData,
     35                              inputLevelState inputState,
     36                              inputButtonPos inputButton);
     37 void registerDispatcher(xtotp_Data *appData,
     38                         inputLevelState inputState,
     39                         inputButtonPos inputButton);
     40 
     41 void registerSelectEntry(xtotp_Data *appData,
     42                          inputLevelState inputState,
     43                          inputButtonPos inputButton);
     44 void registerSelect(xtotp_Data *appData,
     45                     inputLevelState inputState,
     46                     inputButtonPos inputButton);
     47 
     48 void registerSetAlgorithmEntry(xtotp_Data *appData,
     49                                inputLevelState inputState,
     50                                inputButtonPos inputButton);
     51 void registerSetAlgorithm(xtotp_Data *appData,
     52                           inputLevelState inputState,
     53                           inputButtonPos inputButton);
     54 
     55 void registerSetSecretEntry(xtotp_Data *appData,
     56                             inputLevelState inputState,
     57                             inputButtonPos inputButton);
     58 void registerSetSecret(xtotp_Data *appData,
     59                        inputLevelState inputState,
     60                        inputButtonPos inputButton);
     61 
     62 void registerSetCodeLengthEntry(xtotp_Data *appData,
     63                                 inputLevelState inputState,
     64                                 inputButtonPos inputButton);
     65 void registerSetCodeLength(xtotp_Data *appData,
     66                            inputLevelState inputState,
     67                            inputButtonPos inputButton);
     68 
     69 void registerSetIntervalEntry(xtotp_Data *appData,
     70                               inputLevelState inputState,
     71                               inputButtonPos inputButton);
     72 void registerSetInterval(xtotp_Data *appData,
     73                          inputLevelState inputState,
     74                          inputButtonPos inputButton);
     75 
     76 void registerSetCurrencyEntry(xtotp_Data *appData,
     77                               inputLevelState inputState,
     78                               inputButtonPos inputButton);
     79 void registerSetCurrency(xtotp_Data *appData,
     80                          inputLevelState inputState,
     81                          inputButtonPos inputButton);
     82 
     83 void registerSetFractionEntry(xtotp_Data *appData,
     84                               inputLevelState inputState,
     85                               inputButtonPos inputButton);
     86 void registerSetFraction(xtotp_Data *appData,
     87                          inputLevelState inputState,
     88                          inputButtonPos inputButton);
     89 
     90 stateHandler registerSecretStates[XTOTP_REGISTER_SECRET_MAX_STATES] = {
     91     [XTOTP_REGISTER_SECRET_DISPATCHER] = {.entryAction =
     92                                               registerDispatcherEntry,
     93                                           .runAction = registerDispatcher},
     94     [XTOTP_REGISTER_SECRET_SELECT] = {.entryAction = registerSelectEntry,
     95                                       .runAction = registerSelect},
     96     [XTOTP_REGISTER_SECRET_SET_ALGORITHM] = {.entryAction =
     97                                                  registerSetAlgorithmEntry,
     98                                              .runAction = registerSetAlgorithm},
     99     [XTOTP_REGISTER_SECRET_SET_SECRET] = {.entryAction = registerSetSecretEntry,
    100                                           .runAction = registerSetSecret},
    101     [XTOTP_REGISTER_SECRET_SET_CODE_LENGTH] = {.entryAction =
    102                                                    registerSetCodeLengthEntry,
    103                                                .runAction =
    104                                                    registerSetCodeLength},
    105     [XTOTP_REGISTER_SECRET_SET_INTERVAL] = {.entryAction =
    106                                                 registerSetIntervalEntry,
    107                                             .runAction = registerSetInterval},
    108     [XTOTP_REGISTER_SECRET_SET_CURRENCY] = {.entryAction =
    109                                                 registerSetCurrencyEntry,
    110                                             .runAction = registerSetCurrency},
    111     [XTOTP_REGISTER_SECRET_SET_FRACTION] = {.entryAction =
    112                                                 registerSetFractionEntry,
    113                                             .runAction = registerSetFraction}};
    114 
    115 void settings_processSecretHandling(xtotp_Data *appData,
    116                                     inputLevelState inputState,
    117                                     inputButtonPos inputButton)
    118 {
    119   xtotp_RegisterSecretStates oldState = appData->currentStates.registerSecret;
    120   if (appData->currentStates.registerSecret >=
    121       XTOTP_REGISTER_SECRET_MAX_STATES) {
    122     appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER;
    123   } else {
    124     registerSecretStates[appData->currentStates.registerSecret].runAction(
    125         appData, inputState, inputButton);
    126   }
    127   if (oldState != appData->currentStates.registerSecret) {
    128     registerSecretStates[appData->currentStates.registerSecret].entryAction(
    129         appData, inputState, inputButton);
    130   }
    131 }
    132 
    133 void registerDispatcherEntry(xtotp_Data *appData,
    134                              inputLevelState inputState,
    135                              inputButtonPos inputButton)
    136 {
    137   // Nothing TO DO
    138   (void)appData;
    139   (void)inputState;
    140   (void)inputButton;
    141 }
    142 
    143 void registerDispatcher(xtotp_Data *appData,
    144                         inputLevelState inputState,
    145                         inputButtonPos inputButton)
    146 {
    147   (void)inputState;
    148   (void)inputButton;
    149   if (crypto_getAlgoInfo(
    150           &appData->cryptoHandler
    151                .algorithms[appData->cryptoHandler.processedAlgorithm]) ==
    152       ALGO_UNINITIALIZED) {
    153     appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_SET_ALGORITHM;
    154   } else {
    155     appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_SELECT;
    156   }
    157   return;
    158 }
    159 
    160 void registerSelectEntry(xtotp_Data *appData,
    161                          inputLevelState inputState,
    162                          inputButtonPos inputButton)
    163 {
    164   (void)inputState;
    165   (void)inputButton;
    166   frontend_createBaseWindow(appData, "Stored Secret", NULL);
    167   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "Selected: %3u",
    168                            appData->cryptoHandler.processedAlgorithm);
    169   xtotpAlgoSettingsType *selectedAlg =
    170       &appData->cryptoHandler
    171            .algorithms[appData->cryptoHandler.processedAlgorithm];
    172   frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "%us %1uD %s",
    173                            selectedAlg->interval, selectedAlg->digits,
    174                            crypto_getAlgoName(selectedAlg));
    175   appData->iDisplay.show();
    176   usedInputNumber = 0;
    177 }
    178 
    179 void registerSelect(xtotp_Data *appData,
    180                     inputLevelState inputState,
    181                     inputButtonPos inputButton)
    182 {
    183   if (INPUT_STATE_RISING_EDGE == inputState) {
    184     switch (inputButton) {
    185     case INPUT_ENTER:
    186       appData->cryptoHandler.usedAlgorithm =
    187           appData->cryptoHandler.processedAlgorithm;
    188       appData->currentStates.base = XTOTP_DISPATCHER;
    189       break;
    190     case INPUT_DEL:
    191       appData->currentStates.settings = SETTINGS_ENTRY;
    192       break;
    193     case INPUT_0:
    194       appData->currentStates.registerSecret =
    195           XTOTP_REGISTER_SECRET_SET_ALGORITHM;
    196       break;
    197     default:
    198       break;
    199     }
    200   }
    201 }
    202 
    203 void registerSetAlgorithmEntry(xtotp_Data *appData,
    204                                inputLevelState inputState,
    205                                inputButtonPos inputButton)
    206 {
    207   (void)inputState;
    208   (void)inputButton;
    209   frontend_createBaseWindow(appData, "Set Alg", NULL);
    210   if (ALGO_UNINITIALIZED ==
    211       crypto_getAlgoInfo(
    212           &appData->cryptoHandler
    213                .algorithms[appData->cryptoHandler.processedAlgorithm])) {
    214     memset(&appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT], 0,
    215            sizeof(xtotpAlgoSettingsType));
    216   } else {
    217     memcpy(&appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT],
    218            &appData->cryptoHandler
    219                 .algorithms[appData->cryptoHandler.processedAlgorithm],
    220            sizeof(xtotpAlgoSettingsType));
    221   }
    222   usedInputNumber =
    223       appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm;
    224   frontend_updateDatafield(
    225       appData, FRONTEND_DATAFIELD_LINE1, "New alg: %3u",
    226       appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm);
    227   frontend_updateDatafield(
    228       appData, FRONTEND_DATAFIELD_LINE2, "%s <=",
    229       crypto_getAlgoName(
    230           &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]));
    231   appData->iDisplay.show();
    232 }
    233 void registerSetAlgorithm(xtotp_Data *appData,
    234                           inputLevelState inputState,
    235                           inputButtonPos inputButton)
    236 {
    237 
    238   if (INPUT_STATE_RISING_EDGE == inputState) {
    239     if (input_readNumber(&usedInputNumber, ALG_MAX_XTOTP_ALGORITHM - 1,
    240                          inputButton)) {
    241       appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm =
    242           (crypto_algorithms)usedInputNumber;
    243       if (ALGO_UNINITIALIZED !=
    244           crypto_getAlgoInfo(
    245               &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT])) {
    246         // Algorithm OK
    247         appData->currentStates.registerSecret =
    248             XTOTP_REGISTER_SECRET_SET_SECRET;
    249         return;
    250       }
    251     }
    252     if (INPUT_DEL == inputButton && 0 == usedInputNumber) {
    253       // return to settings
    254       appData->currentStates.settings = SETTINGS_ENTRY;
    255       return;
    256     }
    257     appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm =
    258         (crypto_algorithms)usedInputNumber;
    259     frontend_updateDatafield(
    260         appData, FRONTEND_DATAFIELD_LINE1, "New alg: %3u",
    261         appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].algorithm);
    262     frontend_updateDatafield(
    263         appData, FRONTEND_DATAFIELD_LINE2, "%s <=",
    264         crypto_getAlgoName(
    265             &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]));
    266     appData->iDisplay.show();
    267   }
    268 }
    269 
    270 void registerSetSecretEntry(xtotp_Data *appData,
    271                             inputLevelState inputState,
    272                             inputButtonPos inputButton)
    273 {
    274   (void)inputState;
    275   (void)inputButton;
    276   if (appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].secretLen !=
    277       0) {
    278     // Secret already stored
    279     frontend_createBaseWindow(appData, "Secret Enter", NULL);
    280     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "Saved secret");
    281     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2,
    282                              "Enter to skip");
    283   } else {
    284     frontend_createBaseWindow(appData, "Secret Enter", NULL);
    285     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1,
    286                              "Enter new secret");
    287   }
    288   input_readBase8(INPUT_NONE, NULL, NULL, 0, true);
    289   appData->iDisplay.show();
    290 }
    291 
    292 void registerSetSecret(xtotp_Data *appData,
    293                        inputLevelState inputState,
    294                        inputButtonPos inputButton)
    295 {
    296   uint8_t *readInput = NULL;
    297   uint8_t inputLength = 0;
    298   if (XTOTP_REACTION_EDGE == inputState) {
    299     if (input_readBase8(inputButton, &readInput, &inputLength,
    300                         TALER_SECRET_BASE8_LENGTH, false) ||
    301         INPUT_ENTER == inputButton) {
    302       xtotpAlgoSettingsType *registerAlgo =
    303           &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT];
    304       uint32_t secretLength = 0;
    305       if (inputLength > 0) {
    306         uint8_t inputAsNum[TALER_SECRET_BASE8_LENGTH];
    307         if (BASEX_OK !=
    308                 base8_stringToNum(inputAsNum, (const char *)readInput) ||
    309             BASEX_OK != base8_decodeNum(registerAlgo->secret, &secretLength,
    310                                         TALER_CURRENCY_LEN, inputAsNum,
    311                                         inputLength)) {
    312           frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    313                                    "SRC ERROR");
    314           appData->iDisplay.show();
    315           return;
    316         }
    317       }
    318       if (inputLength > 0) {
    319         registerAlgo->secretLen = (uint8_t)secretLength;
    320       }
    321       if (registerAlgo->secretLen > 0) {
    322         // Secret accepted, go to next step
    323         appData->currentStates.registerSecret =
    324             XTOTP_REGISTER_SECRET_SET_CODE_LENGTH;
    325         return;
    326       }
    327     }
    328     if (INPUT_DEL == inputButton && 0 == inputLength) {
    329       appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER;
    330       return;
    331     }
    332     // Display current input
    333     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED,
    334                              (char *)readInput);
    335     appData->iDisplay.show();
    336   }
    337 }
    338 
    339 void registerSetCodeLengthEntry(xtotp_Data *appData,
    340                                 inputLevelState inputState,
    341                                 inputButtonPos inputButton)
    342 {
    343   (void)inputState;
    344   (void)inputButton;
    345   const char *menuTitle = "Digits";
    346   xtotpAlgoSettingsType *registerAlgo =
    347       &appData->cryptoHandler
    348            .algorithms[appData->cryptoHandler.processedAlgorithm];
    349   if (ALGO_UNINITIALIZED == crypto_getAlgoInfo(registerAlgo)) {
    350     usedInputNumber = 0;
    351     frontend_createBaseWindow(appData, menuTitle, "Set new:  ");
    352   } else {
    353     usedInputNumber = registerAlgo->digits;
    354     frontend_createBaseWindow(appData, menuTitle, NULL);
    355     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %1d",
    356                              (uint32_t)usedInputNumber);
    357   }
    358   appData->iDisplay.show();
    359   return;
    360 }
    361 void registerSetCodeLength(xtotp_Data *appData,
    362                            inputLevelState inputState,
    363                            inputButtonPos inputButton)
    364 {
    365   if (XTOTP_REACTION_EDGE == inputState) {
    366     if (input_readNumber(&usedInputNumber, TALER_MAX_PASSCODE_LENGTH,
    367                          inputButton)) {
    368       appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT].digits =
    369           (uint8_t)usedInputNumber;
    370       appData->currentStates.registerSecret =
    371           XTOTP_REGISTER_SECRET_SET_INTERVAL;
    372       return;
    373     }
    374     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %1d",
    375                              (uint32_t)usedInputNumber);
    376     appData->iDisplay.show();
    377   }
    378 }
    379 
    380 void registerSetIntervalEntry(xtotp_Data *appData,
    381                               inputLevelState inputState,
    382                               inputButtonPos inputButton)
    383 {
    384   (void)inputState;
    385   (void)inputButton;
    386   const char *menuTitle = "Interval";
    387   xtotpAlgoSettingsType *registerAlgo =
    388       &appData->cryptoHandler
    389            .algorithms[appData->cryptoHandler.processedAlgorithm];
    390   if (ALGO_UNINITIALIZED == crypto_getAlgoInfo(registerAlgo)) {
    391     usedInputNumber = 0;
    392     frontend_createBaseWindow(appData, menuTitle, "Set new:  ");
    393   } else {
    394     usedInputNumber = registerAlgo->interval;
    395     frontend_createBaseWindow(appData, menuTitle, NULL);
    396     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %3d",
    397                              (uint32_t)usedInputNumber);
    398   }
    399   appData->iDisplay.show();
    400   return;
    401 }
    402 void registerSetInterval(xtotp_Data *appData,
    403                          inputLevelState inputState,
    404                          inputButtonPos inputButton)
    405 {
    406   if (XTOTP_REACTION_EDGE == inputState) {
    407     if (input_readNumber(&usedInputNumber, UINT8_MAX, inputButton)) {
    408       xtotpAlgoSettingsType *newAlgo =
    409           &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT];
    410       newAlgo->interval = (uint8_t)usedInputNumber;
    411       switch (crypto_getAlgoInfo(newAlgo)) {
    412       case ALGO_TOTP:
    413         // Registration finished
    414         crypto_initAlgoSettings(
    415             &appData->cryptoHandler
    416                  .algorithms[appData->cryptoHandler.processedAlgorithm],
    417             newAlgo->secret, newAlgo->secretLen, SECRET_BASE_BYTE,
    418             newAlgo->interval, newAlgo->digits, newAlgo->algorithm, NULL, 0,
    419             NULL);
    420         appData->currentStates.registerSecret =
    421             XTOTP_REGISTER_SECRET_DISPATCHER;
    422         return;
    423       case ALGO_XTOTP:
    424         // Go to register currency
    425         appData->currentStates.registerSecret =
    426             XTOTP_REGISTER_SECRET_SET_CURRENCY;
    427         return;
    428       default:
    429         // An error occurs
    430         appData->currentStates.settings = SETTINGS_ENTRY;
    431         return;
    432       }
    433     }
    434     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set: %3d",
    435                              (uint32_t)usedInputNumber);
    436     appData->iDisplay.show();
    437   }
    438 }
    439 
    440 void registerSetCurrencyEntry(xtotp_Data *appData,
    441                               inputLevelState inputState,
    442                               inputButtonPos inputButton)
    443 {
    444   (void)inputState;
    445   (void)inputButton;
    446   frontend_createBaseWindow(appData, "Currency", NULL);
    447   if (strlen((const char *)appData->cryptoHandler
    448                  .algorithms[CRYPTO_NEW_SECRET_SEGMENT]
    449                  .xTalerData.currency) == 0) {
    450     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "B8:%16s", " ");
    451     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "=>:%16s", " ");
    452   } else {
    453     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "Registered:");
    454     frontend_updateDatafield(
    455         appData, FRONTEND_DATAFIELD_LINE2, "%s",
    456         appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]
    457             .xTalerData.currency);
    458   }
    459   input_readBase8(INPUT_NONE, NULL, NULL, 0, true);
    460   appData->iDisplay.show();
    461 }
    462 void registerSetCurrency(xtotp_Data *appData,
    463                          inputLevelState inputState,
    464                          inputButtonPos inputButton)
    465 {
    466 #define CURRENCY_LENGTH_AS_BASE8                                               \
    467   ((TALER_CURRENCY_LEN * 8 + 2) / BASE8_BIT_LENGTH)
    468 
    469   if (XTOTP_REACTION_EDGE == inputState) {
    470     uint8_t *inputString;
    471     uint8_t inputLength = 0;
    472     bool readState = input_readBase8(inputButton, &inputString, &inputLength,
    473                                      CURRENCY_LENGTH_AS_BASE8, false);
    474     if (INPUT_ENTER == inputButton &&
    475         0 != strlen((const char *)appData->cryptoHandler
    476                         .algorithms[CRYPTO_NEW_SECRET_SEGMENT]
    477                         .xTalerData.currency) &&
    478         0 == inputLength) {
    479       // Take current stored string
    480       appData->currentStates.registerSecret =
    481           XTOTP_REGISTER_SECRET_SET_FRACTION;
    482       return;
    483     }
    484     inputString[inputLength] = '\0';
    485     uint8_t inputAsNum[CURRENCY_LENGTH_AS_BASE8];
    486     uint8_t newCurrency[TALER_CURRENCY_LEN] = "";
    487     uint32_t currencyLength = 0;
    488     baseX_returnType retState = BASEX_OK;
    489     if (BASEX_OK ==
    490         (retState = base8_stringToNum(inputAsNum, (const char *)inputString))) {
    491       retState = base8_decodeNum(newCurrency, &currencyLength,
    492                                  CURRENCY_LENGTH_AS_BASE8, inputAsNum,
    493                                  (const uint32_t)inputLength);
    494       if (BASEX_OK == retState && readState) {
    495         newCurrency[currencyLength] = '\0';
    496         memcpy(appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT]
    497                    .xTalerData.currency,
    498                newCurrency, currencyLength + 1);
    499         appData->currentStates.registerSecret =
    500             XTOTP_REGISTER_SECRET_SET_FRACTION;
    501         return;
    502       }
    503     }
    504     if (retState != BASEX_OK) {
    505       // Error, copy error msg
    506       strncpy((char *)newCurrency, "SRC ERROR", TALER_CURRENCY_LEN);
    507     }
    508     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, "B8:%16s",
    509                              inputString);
    510     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, "=>:%16s",
    511                              newCurrency);
    512     appData->iDisplay.show();
    513   }
    514 }
    515 
    516 void registerSetFractionEntry(xtotp_Data *appData,
    517                               inputLevelState inputState,
    518                               inputButtonPos inputButton)
    519 {
    520   (void)inputState;
    521   (void)inputButton;
    522   xtotpAlgoSettingsType *registerAlgo =
    523       &appData->cryptoHandler
    524            .algorithms[appData->cryptoHandler.processedAlgorithm];
    525   frontend_createBaseWindow(appData, "Fraction", NULL);
    526   if (ALGO_UNINITIALIZED == crypto_getAlgoInfo(registerAlgo)) {
    527     usedInputNumber = 0;
    528     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set:%*s", 6,
    529                              " ");
    530   } else {
    531     usedInputNumber = registerAlgo->xTalerData.fraction;
    532     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set:%*d", 6,
    533                              (uint32_t)usedInputNumber);
    534   }
    535   appData->iDisplay.show();
    536 }
    537 void registerSetFraction(xtotp_Data *appData,
    538                          inputLevelState inputState,
    539                          inputButtonPos inputButton)
    540 {
    541   if (XTOTP_REACTION_EDGE == inputState) {
    542     if (input_readNumber(&usedInputNumber, TALER_AMOUNT_FRAC_BASE,
    543                          inputButton) ||
    544         // 0 Also allowed for no fraction
    545         (0 == usedInputNumber && INPUT_ENTER == inputButton)) {
    546       xtotpAlgoSettingsType *newAlgo =
    547           &appData->cryptoHandler.algorithms[CRYPTO_NEW_SECRET_SEGMENT];
    548       newAlgo->xTalerData.fraction = (uint32_t)usedInputNumber;
    549       // Finish registration
    550       crypto_initAlgoSettings(
    551           &appData->cryptoHandler
    552                .algorithms[appData->cryptoHandler.processedAlgorithm],
    553           newAlgo->secret, newAlgo->secretLen, SECRET_BASE_BYTE,
    554           newAlgo->interval, newAlgo->digits, newAlgo->algorithm,
    555           newAlgo->xTalerData.currency, newAlgo->xTalerData.fraction, NULL);
    556       appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER;
    557       return;
    558     }
    559     frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, "Set:%*d", 6,
    560                              (uint32_t)usedInputNumber);
    561     appData->iDisplay.show();
    562   }
    563 }