metroflip_scene_ravkav.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #include "../metroflip_i.h"
  2. #include <datetime.h>
  3. #include <dolphin/dolphin.h>
  4. #include <locale/locale.h>
  5. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  6. #define Metroflip_POLLER_MAX_BUFFER_SIZE 1024
  7. #define TAG "Metroflip:Scene:RavKav"
  8. #define epoch_ravkav 852073200
  9. uint8_t apdu_success[] = {0x90, 0x00};
  10. // balance
  11. uint8_t select_balance_file[] = {0x94, 0xA4, 0x00, 0x00, 0x02, 0x20, 0x2A, 0x00};
  12. uint8_t read_balance[] = {0x94, 0xb2, 0x01, 0x04, 0x1D};
  13. // balance
  14. uint8_t select_events_aid[] = {0x94, 0xA4, 0x00, 0x00, 0x02, 0x20, 0x10, 0x00};
  15. uint8_t read_event[] = {0x00, 0xb2, 0x01, 0x04, 0x1D};
  16. void locale_format_datetime_cat(FuriString* out, const DateTime* dt) {
  17. // helper to print datetimes
  18. FuriString* s = furi_string_alloc();
  19. LocaleDateFormat date_format = locale_get_date_format();
  20. const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/";
  21. locale_format_date(s, dt, date_format, separator);
  22. furi_string_cat(out, s);
  23. locale_format_time(s, dt, locale_get_time_format(), false);
  24. furi_string_cat_printf(out, " ");
  25. furi_string_cat(out, s);
  26. furi_string_free(s);
  27. }
  28. void byte_to_binary(uint8_t byte, char* bits) {
  29. for(int i = 7; i >= 0; i--) {
  30. bits[7 - i] = (byte & (1 << i)) ? '1' : '0';
  31. }
  32. bits[8] = '\0';
  33. }
  34. int binary_to_decimal(const char binary[]) {
  35. int decimal = 0;
  36. int length = strlen(binary);
  37. for(int i = 0; i < length; i++) {
  38. decimal = decimal * 2 + (binary[i] - '0');
  39. }
  40. return decimal;
  41. }
  42. void metroflip_charliecard_ravkav_widget_callback(
  43. GuiButtonType result,
  44. InputType type,
  45. void* context) {
  46. Metroflip* app = context;
  47. UNUSED(result);
  48. if(type == InputTypeShort) {
  49. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  50. }
  51. }
  52. static NfcCommand metroflip_scene_ravkav_poller_callback(NfcGenericEvent event, void* context) {
  53. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  54. NfcCommand next_command = NfcCommandContinue;
  55. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  56. Metroflip* app = context;
  57. FuriString* parsed_data = furi_string_alloc();
  58. Widget* widget = app->widget;
  59. furi_string_reset(app->text_box_store);
  60. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  61. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  62. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  63. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  64. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  65. if(stage == MetroflipPollerEventTypeStart) {
  66. nfc_device_set_data(
  67. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  68. Iso14443_4bError error;
  69. size_t response_length = 0;
  70. do {
  71. // Select file of balance
  72. bit_buffer_append_bytes(
  73. tx_buffer, select_balance_file, sizeof(select_balance_file));
  74. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  75. if(error != Iso14443_4bErrorNone) {
  76. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  77. stage = MetroflipPollerEventTypeFail;
  78. view_dispatcher_send_custom_event(
  79. app->view_dispatcher, MetroflipCustomEventPollerFail);
  80. break;
  81. }
  82. // Check the response after selecting file
  83. response_length = bit_buffer_get_size_bytes(rx_buffer);
  84. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  85. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  86. FURI_LOG_I(
  87. TAG,
  88. "Select file failed: %02x%02x",
  89. bit_buffer_get_byte(rx_buffer, response_length - 2),
  90. bit_buffer_get_byte(rx_buffer, response_length - 1));
  91. stage = MetroflipPollerEventTypeFail;
  92. view_dispatcher_send_custom_event(
  93. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  94. break;
  95. }
  96. // Now send the read command
  97. bit_buffer_reset(tx_buffer);
  98. bit_buffer_append_bytes(tx_buffer, read_balance, sizeof(read_balance));
  99. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  100. if(error != Iso14443_4bErrorNone) {
  101. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  102. stage = MetroflipPollerEventTypeFail;
  103. view_dispatcher_send_custom_event(
  104. app->view_dispatcher, MetroflipCustomEventPollerFail);
  105. break;
  106. }
  107. // Check the response after reading the file
  108. response_length = bit_buffer_get_size_bytes(rx_buffer);
  109. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  110. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  111. FURI_LOG_I(
  112. TAG,
  113. "Read file failed: %02x%02x",
  114. bit_buffer_get_byte(rx_buffer, response_length - 2),
  115. bit_buffer_get_byte(rx_buffer, response_length - 1));
  116. stage = MetroflipPollerEventTypeFail;
  117. view_dispatcher_send_custom_event(
  118. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  119. break;
  120. }
  121. // Process the response data
  122. if(response_length < 3) {
  123. FURI_LOG_I(TAG, "Response too short: %d bytes", response_length);
  124. stage = MetroflipPollerEventTypeFail;
  125. view_dispatcher_send_custom_event(
  126. app->view_dispatcher, MetroflipCustomEventPollerFail);
  127. break;
  128. }
  129. uint32_t value = 0;
  130. for(uint8_t i = 0; i < 3; i++) {
  131. value = (value << 8) | bit_buffer_get_byte(rx_buffer, i);
  132. }
  133. float result = value / 100.0f;
  134. FURI_LOG_I(TAG, "Value: %.2f ILS", (double)result);
  135. furi_string_printf(parsed_data, "\e#Rav-Kav:\n");
  136. furi_string_cat_printf(parsed_data, "Card Type: Anonymous\n");
  137. furi_string_cat_printf(parsed_data, "Balance: %.2f ILS\n", (double)result);
  138. // Select app for events
  139. bit_buffer_reset(tx_buffer);
  140. bit_buffer_append_bytes(tx_buffer, select_events_aid, sizeof(select_events_aid));
  141. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  142. if(error != Iso14443_4bErrorNone) {
  143. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  144. stage = MetroflipPollerEventTypeFail;
  145. view_dispatcher_send_custom_event(
  146. app->view_dispatcher, MetroflipCustomEventPollerFail);
  147. break;
  148. }
  149. // Check the response after selecting app
  150. response_length = bit_buffer_get_size_bytes(rx_buffer);
  151. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  152. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  153. FURI_LOG_I(
  154. TAG,
  155. "Select events app failed: %02x%02x",
  156. bit_buffer_get_byte(rx_buffer, response_length - 2),
  157. bit_buffer_get_byte(rx_buffer, response_length - 1));
  158. stage = MetroflipPollerEventTypeFail;
  159. view_dispatcher_send_custom_event(
  160. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  161. break;
  162. }
  163. // Now send the read command
  164. for(size_t i = 1; i < 7; i++) {
  165. read_event[2] = i;
  166. bit_buffer_reset(tx_buffer);
  167. bit_buffer_append_bytes(tx_buffer, read_event, sizeof(read_event));
  168. error =
  169. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  170. if(error != Iso14443_4bErrorNone) {
  171. FURI_LOG_I(
  172. TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  173. stage = MetroflipPollerEventTypeFail;
  174. view_dispatcher_send_custom_event(
  175. app->view_dispatcher, MetroflipCustomEventPollerFail);
  176. break;
  177. }
  178. // Check the response after reading the file
  179. response_length = bit_buffer_get_size_bytes(rx_buffer);
  180. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  181. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  182. FURI_LOG_I(
  183. TAG,
  184. "Read file failed: %02x%02x",
  185. bit_buffer_get_byte(rx_buffer, response_length - 2),
  186. bit_buffer_get_byte(rx_buffer, response_length - 1));
  187. stage = MetroflipPollerEventTypeFail;
  188. view_dispatcher_send_custom_event(
  189. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  190. break;
  191. }
  192. char bit_representation
  193. [response_length * 8 + 1]; // Total bits in the response (each byte = 8 bits)
  194. bit_representation[0] = '\0'; // Initialize the string to empty
  195. for(size_t i = 0; i < response_length; i++) {
  196. char bits[9]; // Temporary string for each byte (8 bits + null terminator)
  197. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  198. byte_to_binary(byte, bits);
  199. strcat(bit_representation, bits); // Append binary string to the result
  200. }
  201. int start = 23, end = 52;
  202. char bit_slice[end - start + 2];
  203. strncpy(bit_slice, bit_representation + start, end - start + 1);
  204. bit_slice[end - start + 1] = '\0';
  205. int decimal_value = binary_to_decimal(bit_slice);
  206. uint64_t result_timestamp = decimal_value + epoch_ravkav + (3600 * 3);
  207. DateTime dt = {0};
  208. datetime_timestamp_to_datetime(result_timestamp, &dt);
  209. furi_string_cat_printf(parsed_data, "\nEvent 0%d:\n", i);
  210. locale_format_datetime_cat(parsed_data, &dt);
  211. furi_string_cat_printf(parsed_data, "\n\n");
  212. }
  213. widget_add_text_scroll_element(
  214. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  215. widget_add_button_element(
  216. widget,
  217. GuiButtonTypeRight,
  218. "Exit",
  219. metroflip_charliecard_ravkav_widget_callback,
  220. app);
  221. furi_string_free(parsed_data);
  222. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  223. metroflip_app_blink_stop(app);
  224. stage = MetroflipPollerEventTypeSuccess;
  225. next_command = NfcCommandStop;
  226. } while(false);
  227. if(stage != MetroflipPollerEventTypeSuccess) {
  228. next_command = NfcCommandStop;
  229. }
  230. }
  231. }
  232. bit_buffer_free(tx_buffer);
  233. bit_buffer_free(rx_buffer);
  234. return next_command;
  235. }
  236. void metroflip_scene_ravkav_on_enter(void* context) {
  237. Metroflip* app = context;
  238. dolphin_deed(DolphinDeedNfcRead);
  239. // Setup view
  240. Popup* popup = app->popup;
  241. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  242. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  243. // Start worker
  244. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  245. nfc_scanner_alloc(app->nfc);
  246. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  247. nfc_poller_start(app->poller, metroflip_scene_ravkav_poller_callback, app);
  248. metroflip_app_blink_start(app);
  249. }
  250. bool metroflip_scene_ravkav_on_event(void* context, SceneManagerEvent event) {
  251. Metroflip* app = context;
  252. bool consumed = false;
  253. if(event.type == SceneManagerEventTypeCustom) {
  254. if(event.event == MetroflipPollerEventTypeCardDetect) {
  255. Popup* popup = app->popup;
  256. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  257. consumed = true;
  258. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  259. Popup* popup = app->popup;
  260. popup_set_header(popup, "No\nRecord\nFile", 68, 30, AlignLeft, AlignTop);
  261. consumed = true;
  262. } else if(event.event == MetroflipCustomEventPollerFail) {
  263. Popup* popup = app->popup;
  264. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  265. consumed = true;
  266. }
  267. } else if(event.type == SceneManagerEventTypeBack) {
  268. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  269. consumed = true;
  270. }
  271. return consumed;
  272. }
  273. void metroflip_scene_ravkav_on_exit(void* context) {
  274. Metroflip* app = context;
  275. if(app->poller) {
  276. nfc_poller_stop(app->poller);
  277. nfc_poller_free(app->poller);
  278. }
  279. metroflip_app_blink_stop(app);
  280. widget_reset(app->widget);
  281. // Clear view
  282. popup_reset(app->popup);
  283. }