generate_totp_code.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #include "generate_totp_code.h"
  2. #include "../../services/crypto/crypto.h"
  3. #include "../../services/totp/totp.h"
  4. #include "../../services/convert/convert.h"
  5. #include <furi_hal_rtc.h>
  6. #include <memset_s.h>
  7. #define ONE_SEC_MS (1000)
  8. static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
  9. static void
  10. int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) {
  11. str[len] = '\0';
  12. if(i_token_code == OTP_ERROR) {
  13. memset(&str[0], '-', len);
  14. } else {
  15. if(algo == STEAM) {
  16. for(uint8_t i = 0; i < len; i++) {
  17. str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26];
  18. i_token_code = i_token_code / 26;
  19. }
  20. } else {
  21. for(int8_t i = len - 1; i >= 0; i--) {
  22. str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
  23. i_token_code = i_token_code / 10;
  24. }
  25. }
  26. }
  27. }
  28. static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
  29. switch(algo) {
  30. case SHA1:
  31. case STEAM:
  32. return TOTP_ALGO_SHA1;
  33. case SHA256:
  34. return TOTP_ALGO_SHA256;
  35. case SHA512:
  36. return TOTP_ALGO_SHA512;
  37. default:
  38. break;
  39. }
  40. return NULL;
  41. }
  42. static void generate_totp_code(
  43. TotpGenerateCodeWorkerContext* context,
  44. const TokenInfo* token_info,
  45. uint32_t current_ts) {
  46. if(token_info->token != NULL && token_info->token_length > 0) {
  47. size_t key_length;
  48. uint8_t* key = totp_crypto_decrypt(
  49. token_info->token, token_info->token_length, context->iv, &key_length);
  50. int_token_to_str(
  51. totp_at(
  52. get_totp_algo_impl(token_info->algo),
  53. key,
  54. key_length,
  55. current_ts,
  56. context->timezone_offset,
  57. token_info->duration),
  58. context->code_buffer,
  59. token_info->digits,
  60. token_info->algo);
  61. memset_s(key, key_length, 0, key_length);
  62. free(key);
  63. } else {
  64. int_token_to_str(0, context->code_buffer, token_info->digits, token_info->algo);
  65. }
  66. }
  67. static int32_t totp_generate_worker_callback(void* context) {
  68. furi_check(context);
  69. TotpGenerateCodeWorkerContext* t_context = context;
  70. while(true) {
  71. uint32_t flags = furi_thread_flags_wait(
  72. TotpGenerateCodeWorkerEventStop | TotpGenerateCodeWorkerEventForceUpdate,
  73. FuriFlagWaitAny,
  74. ONE_SEC_MS);
  75. if(flags ==
  76. (uint32_t)
  77. FuriFlagErrorTimeout) { // If timeout, consider as no error, as we expect this and can handle gracefully
  78. flags = 0;
  79. }
  80. furi_check((flags & FuriFlagError) == 0); //-V562
  81. if(flags & TotpGenerateCodeWorkerEventStop) break;
  82. const TokenInfo* token_info = *(t_context->token_info);
  83. if(token_info == NULL) {
  84. continue;
  85. }
  86. uint32_t curr_ts = furi_hal_rtc_get_timestamp();
  87. bool time_left = false;
  88. if(flags & TotpGenerateCodeWorkerEventForceUpdate ||
  89. (time_left = (curr_ts % token_info->duration) == 0)) {
  90. if(furi_mutex_acquire(t_context->code_buffer_sync, FuriWaitForever) == FuriStatusOk) {
  91. generate_totp_code(t_context, token_info, curr_ts);
  92. curr_ts = furi_hal_rtc_get_timestamp();
  93. furi_mutex_release(t_context->code_buffer_sync);
  94. if(t_context->on_new_code_generated_handler != NULL) {
  95. (*(t_context->on_new_code_generated_handler))(
  96. time_left, t_context->on_new_code_generated_handler_context);
  97. }
  98. }
  99. }
  100. if(t_context->on_code_lifetime_changed_handler != NULL) {
  101. (*(t_context->on_code_lifetime_changed_handler))(
  102. (float)(token_info->duration - curr_ts % token_info->duration) /
  103. (float)token_info->duration,
  104. t_context->on_code_lifetime_changed_handler_context);
  105. }
  106. }
  107. return 0;
  108. }
  109. TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
  110. char* code_buffer,
  111. TokenInfo** token_info,
  112. FuriMutex* code_buffer_sync,
  113. float timezone_offset,
  114. uint8_t* iv) {
  115. TotpGenerateCodeWorkerContext* context = malloc(sizeof(TotpGenerateCodeWorkerContext));
  116. furi_check(context != NULL);
  117. context->code_buffer = code_buffer;
  118. context->token_info = token_info;
  119. context->code_buffer_sync = code_buffer_sync;
  120. context->timezone_offset = timezone_offset;
  121. context->iv = iv;
  122. context->thread = furi_thread_alloc();
  123. furi_thread_set_name(context->thread, "TOTPGenerateWorker");
  124. furi_thread_set_stack_size(context->thread, 2048);
  125. furi_thread_set_context(context->thread, context);
  126. furi_thread_set_callback(context->thread, totp_generate_worker_callback);
  127. furi_thread_start(context->thread);
  128. return context;
  129. }
  130. void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context) {
  131. furi_check(context != NULL);
  132. furi_thread_flags_set(furi_thread_get_id(context->thread), TotpGenerateCodeWorkerEventStop);
  133. furi_thread_join(context->thread);
  134. furi_thread_free(context->thread);
  135. free(context);
  136. }
  137. void totp_generate_code_worker_notify(
  138. TotpGenerateCodeWorkerContext* context,
  139. TotpGenerateCodeWorkerEvent event) {
  140. furi_check(context != NULL);
  141. furi_thread_flags_set(furi_thread_get_id(context->thread), event);
  142. }
  143. void totp_generate_code_worker_set_code_generated_handler(
  144. TotpGenerateCodeWorkerContext* context,
  145. TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler,
  146. void* on_new_code_generated_handler_context) {
  147. furi_check(context != NULL);
  148. context->on_new_code_generated_handler = on_new_code_generated_handler;
  149. context->on_new_code_generated_handler_context = on_new_code_generated_handler_context;
  150. }
  151. void totp_generate_code_worker_set_lifetime_changed_handler(
  152. TotpGenerateCodeWorkerContext* context,
  153. TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler,
  154. void* on_code_lifetime_changed_handler_context) {
  155. furi_check(context != NULL);
  156. context->on_code_lifetime_changed_handler = on_code_lifetime_changed_handler;
  157. context->on_code_lifetime_changed_handler_context = on_code_lifetime_changed_handler_context;
  158. }