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, ¤cyLength, 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 }