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 }