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 }