taler-xotp_fw

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

xtotp.c (10948B)


      1 /**
      2  * @file xtotp.c
      3  * @author Adrian STEINER (steia19@bfh.ch)
      4  * @brief xTOTP application data and interface for initialising and processing
      5  * @version 0.1
      6  * @date 13-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 "xtotp.h"
     25 #include <stdlib.h>
     26 
     27 #include "logger.h"
     28 
     29 #include "frontend.h"
     30 #include "xtotpConfig.h"
     31 #include "xtotpStateMachine.h"
     32 #include "xtotpUtil.h"
     33 
     34 uint32_t oldTick = 0;
     35 uint32_t stopTick = 0;
     36 uint32_t timeoutTick = 0;
     37 
     38 /**
     39  * @brief Function to sample the synchronizing data if enables
     40  *
     41  * @param appData Application data
     42  * @param diffTick Tick difference to increment the sync time
     43  */
     44 void sample_synchronize(xtotp_Data *appData, uint32_t diffTick);
     45 
     46 uint8_t xtotp_init(
     47     /* app data*/
     48     xtotp_Data *appData,
     49     /* Crypto interface */
     50     cryptoCalcTotpCB computeTotpCB,
     51     cryptoCalcXTotpCB computeXTotpCB,
     52     /* Device interface */
     53     deviceGetTickCB getTickCB,
     54     deviceShutdownCB shutdownCB,
     55     /* auto pay*/
     56     autoPayPwrCB autoPayPowerOnCB,
     57     autoPayPwrCB autoPayPowerOffCB,
     58     autoPayGetPasscodes autoPayGetPasscodesCB,
     59     autoPayArePasscodesReceived autoPayArePasscodesReceivedCB,
     60     autoPaySendURL autoPaySendUrlCB,
     61     autoPayIsURLRead autoPayIsURLReadCB,
     62     /* display interface */
     63     pbm_image *displayImage,
     64     pbm_font *smallFont,
     65     pbm_font *bigFont,
     66     displayPwrCB displayPowerOnCB,
     67     displayPwrCB displayPowerOffCB,
     68     displayShowCB dispShowCB,
     69     uint8_t symbolCharge,
     70     uint8_t symbolSynchronize,
     71     uint8_t symbolBatteryStart,
     72     /* input interface */
     73     inputPwrCB inputPowerOnCB,
     74     inputPwrCB inputPowerOffCB,
     75     inputSampleCB inputReadInputCB,
     76     /* clock interface */
     77     clockPwrCB clockPowerOnCB,
     78     clockPwrCB clockPowerOffCB,
     79     clockGetTimeCB clockGetUnixTimeCB,
     80     clockSetTimeCB clockSetUnixTimeCB,
     81     /* sync interface */
     82     syncPwrCB syncPowerOnCB,
     83     syncPwrCB syncpowerOffCB,
     84     synchronizeCB syncCB,
     85     /* bat meas interface */
     86     batMeasPwrCB batMeasPowerOnCB,
     87     batMeasPwrCB batMeasPowerOffCB,
     88     getBatteryStateCB batMeasGetStateCB)
     89 {
     90 
     91   uint8_t returnValue = EXIT_SUCCESS;
     92 
     93   /* Initial states */
     94   appData->currentStates.base = XTOTP_STARTUP;
     95   appData->currentStates.settings = SETTINGS_ENTRY;
     96   appData->currentStates.additionalSettings = ADDITIONAL_CLOCK;
     97   appData->currentStates.registerSecret = XTOTP_REGISTER_SECRET_DISPATCHER;
     98 
     99   appData->timeOutTime = XTOTP_DEFAULT_TIMEOUT;
    100   appData->stopTimeEnterButton = XTOTP_STOP_TIME_BUTTON;
    101 
    102   appData->processData.enteredAmount = 0;
    103   appData->processData.lastCode = 0;
    104   appData->processData.lastNeededTime = 0;
    105 
    106   appData->secFlag = false;
    107 
    108   /* Crypto interface */
    109   if (crypto_initInterface(&appData->iCrypto, computeTotpCB, computeXTotpCB)) {
    110     returnValue = EXIT_FAILURE;
    111   }
    112 
    113   crypto_setMerchantData(&appData->cryptoHandler, XTOTP_DEVICE_ID,
    114                          XTOTP_MERCHANT_BACKEND);
    115 
    116   uint8_t secret[] = {0xdc, 0x73, 0x17, 0x1d, 0xc7, 0xd1, 0xfa,
    117                       0x97, 0xa6, 0x8,  0xa,  0x8c, 0x74, 0x64,
    118                       0xdf, 0x83, 0xda, 0x6b, 0x2a, 0x72};
    119   // uint8_t base8Secret[] =
    120   // "132378488654568243857521365366371461664865781465232244";
    121   appData->cryptoHandler.usedAlgorithm = 1;
    122   uint8_t secretBase32[] = "55IOFHFBZVFC7KTBD4XTN7SH5ZZTNLKS";
    123   crypto_initAlgoSettings(
    124       &appData->cryptoHandler.algorithms[appData->cryptoHandler.usedAlgorithm],
    125       secretBase32, sizeof(secretBase32), SECRET_BASE_32, 30, 8, ALG_XTOTP_SHA1,
    126       (uint8_t *)"KUDOS", 100, (const uint8_t *)"xTOTP_test");
    127 
    128   crypto_initAlgoSettings(
    129       &appData->cryptoHandler
    130            .algorithms[appData->cryptoHandler.usedAlgorithm + 1],
    131       secret, sizeof(secret), SECRET_BASE_BYTE, 30, 4, ALG_XTOTP_SHA1,
    132       (uint8_t *)"KUDOS", 0, NULL);
    133 
    134   // uint8_t base8Secret[] =
    135   // "132378488654568243857521365366371461664865781465232244";
    136 
    137   uint8_t secret2[] = "BCW575RZPFV42CCWFNLA5C27WNYOYKEW";
    138   crypto_initAlgoSettings(
    139       &appData->cryptoHandler.algorithms[XTOTP_STORABLE_SECRETS], secret2,
    140       sizeof(secret2), SECRET_BASE_32, 60, 6, ALG_TOTP_SHA1, NULL, 0, NULL);
    141 
    142   /* Autopay interface */
    143   if (autoPay_initInterface(&appData->iAutoPay, autoPayPowerOnCB,
    144                             autoPayPowerOffCB, autoPayGetPasscodesCB,
    145                             autoPayArePasscodesReceivedCB, autoPaySendUrlCB,
    146                             autoPayIsURLReadCB)) {
    147     returnValue = EXIT_FAILURE;
    148   }
    149 
    150   /* Device interface */
    151   if (device_initInterface(&appData->iDevice, getTickCB, shutdownCB)) {
    152     returnValue = EXIT_FAILURE;
    153   }
    154 
    155   /* HW interface*/
    156   if (display_initInterface(&appData->iDisplay, displayImage, smallFont,
    157                             bigFont, displayPowerOnCB, displayPowerOffCB,
    158                             dispShowCB, symbolCharge, symbolSynchronize,
    159                             symbolBatteryStart, XTOTP_DEFAULT_THEME)) {
    160     returnValue = EXIT_FAILURE;
    161   }
    162   if (input_initInterface(&appData->iInput, inputPowerOnCB, inputPowerOffCB,
    163                           inputReadInputCB)) {
    164     returnValue = EXIT_FAILURE;
    165   }
    166   if (clock_initInterface(&appData->iClock, clockPowerOnCB, clockPowerOffCB,
    167                           clockGetUnixTimeCB, clockSetUnixTimeCB)) {
    168     returnValue = EXIT_FAILURE;
    169   }
    170 
    171   if (sync_initInterface(&appData->iSync, syncPowerOnCB, syncpowerOffCB, syncCB,
    172                          FIRMWARE_COMPILE_TIME)) {
    173     returnValue = EXIT_FAILURE;
    174   }
    175 
    176   if (batMeas_initInterface(&appData->iBatMeas, batMeasPowerOnCB,
    177                             batMeasPowerOffCB, batMeasGetStateCB)) {
    178     returnValue = EXIT_FAILURE;
    179   }
    180 
    181   if (returnValue) {
    182     return returnValue;
    183   }
    184 
    185   appData->iClock.powerOn();
    186   appData->iClock.setUnixTime(FIRMWARE_COMPILE_TIME);
    187   appData->iClock.powerOff();
    188 
    189   xtotp_process(appData);
    190   return EXIT_SUCCESS;
    191 }
    192 
    193 uint8_t xtotp_process(xtotp_Data *appData)
    194 {
    195   uint32_t currentTick = appData->iDevice.getTick();
    196   uint32_t diffTick = getTickDiff(currentTick, oldTick);
    197   if (currentTick % APP_SEC_BASE) {
    198     appData->secFlag = true;
    199   }
    200   if (appData->currentStates.base == XTOTP_STARTUP) {
    201     diffTick = XTOTP_SAMPLE_TIME;
    202     timeoutTick = 0;
    203     stopTick = 0;
    204   }
    205 
    206   if (diffTick >= XTOTP_SAMPLE_TIME) {
    207     oldTick = currentTick;
    208     inputButtonPos buttonPos;
    209     inputLevelState inputState = appData->iInput.readInput(&buttonPos);
    210     LOGGER_TRACE("Pressed button %u with state %u", buttonPos, inputState);
    211 
    212     // Reset activity timeout
    213     if (inputState == INPUT_STATE_RISING_EDGE ||
    214         inputState == INPUT_STATE_FALLING_EDGE) {
    215       // Recognise activity
    216       timeoutTick = 0;
    217     } else {
    218       timeoutTick += diffTick;
    219     }
    220     // Check timeout activity if enabled (time > 0)
    221     if (appData->timeOutTime > 0 && timeoutTick >= appData->timeOutTime) {
    222       // Timeout needed => shutdown
    223       timeoutTick = 0;
    224       appData->currentStates.base = XTOTP_SLEEP;
    225       LOGGER_INFO("Shutdown during inactivity");
    226     }
    227 
    228     if (INPUT_ENTER == buttonPos) {
    229       switch (inputState) {
    230       case INPUT_STATE_RISING_EDGE:
    231         stopTick = 0;
    232         break;
    233       case INPUT_STATE_HIGH:
    234         stopTick += diffTick;
    235         if (stopTick >= appData->stopTimeEnterButton) {
    236           stopTick = 0;
    237           LOGGER_INFO("Shutdown device due button pressing");
    238           appData->currentStates.base = XTOTP_SLEEP;
    239         }
    240         break;
    241       default:
    242         // Nothing to do
    243         break;
    244       }
    245     }
    246 
    247     if (!batMeas_measure(&appData->iBatMeas, currentTick,
    248                          XTOTP_BATTERY_CHECK_TIME)) {
    249       frontend_writeStatusIcons(appData);
    250       appData->iDisplay.show();
    251     };
    252 
    253     sample_synchronize(appData, diffTick);
    254 
    255     xtotp_processStateMachine(appData, inputState, buttonPos);
    256     appData->secFlag = false;
    257   }
    258 
    259   return EXIT_SUCCESS;
    260 }
    261 
    262 void sample_synchronize(xtotp_Data *appData, uint32_t diffTick)
    263 {
    264   static uint32_t syncErrorTime = 0;
    265   if (appData->iSync.isSynchronizing) {
    266     if (batMeas_getBatteryPercentState(&appData->iBatMeas) <
    267         XTOTP_SYNC_MIN_BATTERY_LEVEL) {
    268       sync_abort(&appData->iSync);
    269       syncErrorTime = 0;
    270       appData->iSync.syncDuration = UINT32_MAX;
    271       frontend_writeStatusIcons(appData);
    272       appData->processData.updateDisplayData = true;
    273       return;
    274     }
    275     appData->iSync.syncDuration += diffTick;
    276     if (syncErrorTime > XTOTP_SYNC_ERROR_RESET) {
    277       appData->iSync.syncDuration = 0;
    278       appData->iSync.powerOn();
    279       syncErrorTime = 0;
    280     }
    281     timeoutTick = 0; // Do not power off during synchronizing
    282     tinyTimeType syncTime;
    283     switch (appData->iSync.synchronize(&syncTime)) {
    284     case SYNC_WAITING:
    285       LOGGER_TRACE("Waiting for sync data");
    286       syncErrorTime = 0;
    287       break;
    288     case SYNC_TIME_ONLY:
    289       LOGGER_DEBUG("Received only time: %.2d:%.2d:%.2d", syncTime.hour,
    290                    syncTime.min, syncTime.sec);
    291       syncErrorTime = 0;
    292       /* To define what to do */
    293       break;
    294     case SYNC_TIME_AND_DATE:
    295       // Reset time in clock peripheral
    296       appData->iClock.powerOn();
    297       // Calculate new received unix time
    298       uint64_t syncUnixTime = (uint64_t)tiny_getUnixTime(&syncTime);
    299       LOGGER_DEBUG("Received time and date: %.2u:%.2u:%.2u %.2u.%.2u.%.4u",
    300                    syncTime.hour, syncTime.min, syncTime.sec, syncTime.monthDay,
    301                    syncTime.month, syncTime.year);
    302       // Get difference of current time
    303       uint64_t oldClockTime = appData->iClock.getUnixTime();
    304       appData->iClock.setUnixTime(syncUnixTime);
    305       appData->iClock.powerOff();
    306       if (syncUnixTime > oldClockTime) {
    307         appData->iSync.syncDiff = syncUnixTime - oldClockTime;
    308       } else {
    309         appData->iSync.syncDiff = oldClockTime - syncUnixTime;
    310       }
    311       appData->iSync.lastSyncTime = syncUnixTime;
    312       appData->iSync.expectedSyncTime = syncUnixTime + XTOTP_SYNC_UPDATE_PERIOD;
    313       sync_abort(&appData->iSync);
    314       syncErrorTime = 0;
    315       // Update frontend
    316       frontend_writeStatusIcons(appData);
    317       appData->processData.updateDisplayData = true;
    318       appData->iDisplay.show();
    319       break;
    320     case SYNC_ERROR:
    321     default:
    322       syncErrorTime += diffTick;
    323       if (syncErrorTime > XTOTP_SYNC_ERROR_RESET) {
    324         LOGGER_INFO("SYNC failed %u => reset", syncErrorTime);
    325         appData->iSync.powerOff();
    326       }
    327       break;
    328     }
    329   }
    330 }