metroflip_scene_calypso.c 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441
  1. #include "metroflip_scene_calypso.h"
  2. #include "../metroflip_i.h"
  3. #include <datetime.h>
  4. #include <dolphin/dolphin.h>
  5. #include <notification/notification_messages.h>
  6. #include <locale/locale.h>
  7. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  8. #define TAG "Metroflip:Scene:Calypso"
  9. int select_new_app(
  10. int new_app_directory,
  11. int new_app,
  12. BitBuffer* tx_buffer,
  13. BitBuffer* rx_buffer,
  14. Iso14443_4bPoller* iso14443_4b_poller,
  15. Metroflip* app,
  16. MetroflipPollerEventType* stage) {
  17. select_app[5] = new_app_directory;
  18. select_app[6] = new_app;
  19. bit_buffer_reset(tx_buffer);
  20. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  21. FURI_LOG_D(
  22. TAG,
  23. "SEND %02x %02x %02x %02x %02x %02x %02x %02x",
  24. select_app[0],
  25. select_app[1],
  26. select_app[2],
  27. select_app[3],
  28. select_app[4],
  29. select_app[5],
  30. select_app[6],
  31. select_app[7]);
  32. int error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  33. if(error != Iso14443_4bErrorNone) {
  34. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  35. *stage = MetroflipPollerEventTypeFail;
  36. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  37. return error;
  38. }
  39. return 0;
  40. }
  41. int read_new_file(
  42. int new_file,
  43. BitBuffer* tx_buffer,
  44. BitBuffer* rx_buffer,
  45. Iso14443_4bPoller* iso14443_4b_poller,
  46. Metroflip* app,
  47. MetroflipPollerEventType* stage) {
  48. read_file[2] = new_file;
  49. bit_buffer_reset(tx_buffer);
  50. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  51. FURI_LOG_D(
  52. TAG,
  53. "SEND %02x %02x %02x %02x %02x",
  54. read_file[0],
  55. read_file[1],
  56. read_file[2],
  57. read_file[3],
  58. read_file[4]);
  59. Iso14443_4bError error =
  60. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  61. if(error != Iso14443_4bErrorNone) {
  62. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  63. *stage = MetroflipPollerEventTypeFail;
  64. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  65. return error;
  66. }
  67. return 0;
  68. }
  69. int check_response(
  70. BitBuffer* rx_buffer,
  71. Metroflip* app,
  72. MetroflipPollerEventType* stage,
  73. size_t* response_length) {
  74. *response_length = bit_buffer_get_size_bytes(rx_buffer);
  75. if(bit_buffer_get_byte(rx_buffer, *response_length - 2) != apdu_success[0] ||
  76. bit_buffer_get_byte(rx_buffer, *response_length - 1) != apdu_success[1]) {
  77. int error_code_1 = bit_buffer_get_byte(rx_buffer, *response_length - 2);
  78. int error_code_2 = bit_buffer_get_byte(rx_buffer, *response_length - 1);
  79. FURI_LOG_E(TAG, "Select profile app/file failed: %02x%02x", error_code_1, error_code_2);
  80. if(error_code_1 == 0x6a && error_code_2 == 0x82) {
  81. FURI_LOG_E(TAG, "Wrong parameter(s) P1-P2 - File not found");
  82. } else if(error_code_1 == 0x69 && error_code_2 == 0x82) {
  83. FURI_LOG_E(TAG, "Command not allowed - Security status not satisfied");
  84. }
  85. *stage = MetroflipPollerEventTypeFail;
  86. view_dispatcher_send_custom_event(
  87. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  88. return 1;
  89. }
  90. return 0;
  91. }
  92. void update_page_info(void* context, FuriString* parsed_data) {
  93. Metroflip* app = context;
  94. CalypsoContext* ctx = app->calypso_context;
  95. if(ctx->page_id == 0 || ctx->page_id == 1 || ctx->page_id == 2 || ctx->page_id == 3) {
  96. switch(ctx->card->card_type) {
  97. case CALYPSO_CARD_NAVIGO: {
  98. furi_string_cat_printf(
  99. parsed_data,
  100. "\e#%s %u:\n",
  101. get_navigo_type(ctx->card->navigo->holder.card_status),
  102. ctx->card->card_number);
  103. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  104. show_navigo_contract_info(&ctx->card->navigo->contracts[ctx->page_id], parsed_data);
  105. break;
  106. }
  107. case CALYPSO_CARD_OPUS: {
  108. furi_string_cat_printf(parsed_data, "\e#Opus %u:\n", ctx->card->card_number);
  109. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  110. show_opus_contract_info(&ctx->card->opus->contracts[ctx->page_id], parsed_data);
  111. break;
  112. }
  113. default: {
  114. furi_string_cat_printf(parsed_data, "\e#Unknown %u:\n", ctx->card->card_number);
  115. break;
  116. }
  117. }
  118. } else if(ctx->page_id == 4) {
  119. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  120. switch(ctx->card->card_type) {
  121. case CALYPSO_CARD_NAVIGO: {
  122. show_navigo_environment_info(&ctx->card->navigo->environment, parsed_data);
  123. break;
  124. }
  125. case CALYPSO_CARD_OPUS: {
  126. show_opus_environment_info(&ctx->card->opus->environment, parsed_data);
  127. break;
  128. }
  129. default: {
  130. break;
  131. }
  132. }
  133. } else if(ctx->page_id == 5 || ctx->page_id == 6 || ctx->page_id == 7) {
  134. furi_string_cat_printf(parsed_data, "\e#Event %d:\n", ctx->page_id - 4);
  135. switch(ctx->card->card_type) {
  136. case CALYPSO_CARD_NAVIGO: {
  137. show_navigo_event_info(
  138. &ctx->card->navigo->events[ctx->page_id - 5],
  139. ctx->card->navigo->contracts,
  140. parsed_data);
  141. break;
  142. }
  143. case CALYPSO_CARD_OPUS: {
  144. show_opus_event_info(
  145. &ctx->card->opus->events[ctx->page_id - 5],
  146. ctx->card->opus->contracts,
  147. parsed_data);
  148. break;
  149. }
  150. default: {
  151. break;
  152. }
  153. }
  154. }
  155. }
  156. void update_widget_elements(void* context) {
  157. Metroflip* app = context;
  158. CalypsoContext* ctx = app->calypso_context;
  159. Widget* widget = app->widget;
  160. if(ctx->page_id < 7) {
  161. widget_add_button_element(
  162. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  163. } else {
  164. widget_add_button_element(
  165. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  166. }
  167. if(ctx->page_id > 0) {
  168. widget_add_button_element(
  169. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  170. }
  171. }
  172. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  173. Metroflip* app = context;
  174. CalypsoContext* ctx = app->calypso_context;
  175. UNUSED(result);
  176. Widget* widget = app->widget;
  177. if(type == InputTypePress) {
  178. widget_reset(widget);
  179. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  180. if(ctx->page_id > 0) {
  181. if(ctx->page_id == 4 && ctx->card->contracts_count < 4) {
  182. ctx->page_id -= 1;
  183. }
  184. if(ctx->page_id == 3 && ctx->card->contracts_count < 3) {
  185. ctx->page_id -= 1;
  186. }
  187. if(ctx->page_id == 2 && ctx->card->contracts_count < 2) {
  188. ctx->page_id -= 1;
  189. }
  190. ctx->page_id -= 1;
  191. }
  192. FuriString* parsed_data = furi_string_alloc();
  193. // Ensure no nested mutexes
  194. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  195. update_page_info(app, parsed_data);
  196. furi_mutex_release(ctx->mutex);
  197. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  198. // widget_add_icon_element(widget, 0, 0, &I_RFIDDolphinReceive_97x61);
  199. // Ensure no nested mutexes
  200. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  201. update_widget_elements(app);
  202. furi_mutex_release(ctx->mutex);
  203. furi_string_free(parsed_data);
  204. }
  205. }
  206. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  207. Metroflip* app = context;
  208. CalypsoContext* ctx = app->calypso_context;
  209. UNUSED(result);
  210. Widget* widget = app->widget;
  211. if(type == InputTypePress) {
  212. widget_reset(widget);
  213. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  214. if(ctx->page_id < 7) {
  215. if(ctx->page_id == 0 && ctx->card->contracts_count < 2) {
  216. ctx->page_id += 1;
  217. }
  218. if(ctx->page_id == 1 && ctx->card->contracts_count < 3) {
  219. ctx->page_id += 1;
  220. }
  221. if(ctx->page_id == 2 && ctx->card->contracts_count < 4) {
  222. ctx->page_id += 1;
  223. }
  224. ctx->page_id += 1;
  225. } else {
  226. ctx->page_id = 0;
  227. scene_manager_search_and_switch_to_previous_scene(
  228. app->scene_manager, MetroflipSceneStart);
  229. return;
  230. }
  231. FuriString* parsed_data = furi_string_alloc();
  232. // Ensure no nested mutexes
  233. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  234. update_page_info(app, parsed_data);
  235. furi_mutex_release(ctx->mutex);
  236. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  237. // Ensure no nested mutexes
  238. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  239. update_widget_elements(app);
  240. furi_mutex_release(ctx->mutex);
  241. furi_string_free(parsed_data);
  242. }
  243. }
  244. void delay(int milliseconds) {
  245. furi_thread_flags_wait(0, FuriFlagWaitAny, milliseconds);
  246. }
  247. static NfcCommand metroflip_scene_navigo_poller_callback(NfcGenericEvent event, void* context) {
  248. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  249. NfcCommand next_command = NfcCommandContinue;
  250. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  251. Metroflip* app = context;
  252. FuriString* parsed_data = furi_string_alloc();
  253. Widget* widget = app->widget;
  254. furi_string_reset(app->text_box_store);
  255. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  256. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  257. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  258. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  259. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  260. if(stage == MetroflipPollerEventTypeStart) {
  261. // Start Flipper vibration
  262. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  263. notification_message(notification, &sequence_set_vibro_on);
  264. delay(50);
  265. notification_message(notification, &sequence_reset_vibro);
  266. nfc_device_set_data(
  267. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  268. Iso14443_4bError error;
  269. size_t response_length = 0;
  270. do {
  271. // Initialize the card data
  272. CalypsoCardData* card = malloc(sizeof(CalypsoCardData));
  273. // Select app ICC
  274. error = select_new_app(
  275. 0x00, 0x02, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  276. if(error != 0) {
  277. break;
  278. }
  279. // Check the response after selecting app
  280. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  281. break;
  282. }
  283. // Now send the read command for ICC
  284. error = read_new_file(0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  285. if(error != 0) {
  286. break;
  287. }
  288. // Check the response after reading the file
  289. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  290. break;
  291. }
  292. char icc_bit_representation[response_length * 8 + 1];
  293. icc_bit_representation[0] = '\0';
  294. for(size_t i = 0; i < response_length; i++) {
  295. char bits[9];
  296. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  297. byte_to_binary(byte, bits);
  298. strlcat(icc_bit_representation, bits, sizeof(icc_bit_representation));
  299. }
  300. icc_bit_representation[response_length * 8] = '\0';
  301. int start = 128, end = 159;
  302. card->card_number = bit_slice_to_dec(icc_bit_representation, start, end);
  303. // Select app for ticketing
  304. error = select_new_app(
  305. 0x20, 0x00, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  306. if(error != 0) {
  307. FURI_LOG_E(TAG, "Failed to select app for ticketing");
  308. break;
  309. }
  310. // Check the response after selecting app
  311. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  312. FURI_LOG_E(TAG, "Failed to check response after selecting app for ticketing");
  313. break;
  314. }
  315. // Select app for environment
  316. error = select_new_app(
  317. 0x20, 0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  318. if(error != 0) {
  319. break;
  320. }
  321. // Check the response after selecting app
  322. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  323. break;
  324. }
  325. // read file 1
  326. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  327. if(error != 0) {
  328. break;
  329. }
  330. // Check the response after reading the file
  331. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  332. break;
  333. }
  334. char environment_bit_representation[response_length * 8 + 1];
  335. environment_bit_representation[0] = '\0';
  336. for(size_t i = 0; i < response_length; i++) {
  337. char bits[9];
  338. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  339. byte_to_binary(byte, bits);
  340. strlcat(
  341. environment_bit_representation,
  342. bits,
  343. sizeof(environment_bit_representation));
  344. }
  345. // FURI_LOG_I(
  346. // TAG, "Environment bit_representation: %s", environment_bit_representation);
  347. start = 13;
  348. end = 16;
  349. int country_num =
  350. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  351. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  352. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  353. start = 25;
  354. end = 28;
  355. int network_num =
  356. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  357. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  358. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  359. if(guess_card_type(country_num, network_num) != CALYPSO_CARD_UNKNOWN) {
  360. card->card_type = guess_card_type(country_num, network_num);
  361. }
  362. switch(card->card_type) {
  363. case CALYPSO_CARD_NAVIGO: {
  364. card->navigo = malloc(sizeof(NavigoCardData));
  365. card->navigo->environment.country_num = country_num;
  366. card->navigo->environment.network_num = network_num;
  367. CalypsoApp* IntercodeEnvHolderStructure = get_intercode_env_holder_structure();
  368. // EnvApplicationVersionNumber
  369. const char* env_key = "EnvApplicationVersionNumber";
  370. int positionOffset = get_calypso_node_offset(
  371. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  372. int start = positionOffset,
  373. end = positionOffset +
  374. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  375. card->navigo->environment.app_version =
  376. bit_slice_to_dec(environment_bit_representation, start, end);
  377. // EnvApplicationValidityEndDate
  378. env_key = "EnvApplicationValidityEndDate";
  379. positionOffset = get_calypso_node_offset(
  380. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  381. start = positionOffset,
  382. end = positionOffset +
  383. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  384. float decimal_value =
  385. bit_slice_to_dec(environment_bit_representation, start, end);
  386. uint64_t end_validity_timestamp =
  387. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  388. datetime_timestamp_to_datetime(
  389. end_validity_timestamp, &card->navigo->environment.end_dt);
  390. // HolderDataCardStatus
  391. env_key = "HolderDataCardStatus";
  392. positionOffset = get_calypso_node_offset(
  393. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  394. start = positionOffset,
  395. end = positionOffset +
  396. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  397. card->navigo->holder.card_status =
  398. bit_slice_to_dec(environment_bit_representation, start, end);
  399. // HolderDataCommercialID
  400. env_key = "HolderDataCommercialID";
  401. positionOffset = get_calypso_node_offset(
  402. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  403. start = positionOffset,
  404. end = positionOffset +
  405. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  406. card->navigo->holder.commercial_id =
  407. bit_slice_to_dec(environment_bit_representation, start, end);
  408. // Free the calypso structure
  409. free_calypso_structure(IntercodeEnvHolderStructure);
  410. // Select app for contracts
  411. error = select_new_app(
  412. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  413. if(error != 0) {
  414. FURI_LOG_E(TAG, "Failed to select app for contracts");
  415. break;
  416. }
  417. // Check the response after selecting app
  418. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  419. FURI_LOG_E(
  420. TAG, "Failed to check response after selecting app for contracts");
  421. break;
  422. }
  423. // Prepare calypso structure
  424. CalypsoApp* IntercodeContractStructure = get_intercode_contract_structure();
  425. if(!IntercodeContractStructure) {
  426. FURI_LOG_E(TAG, "Failed to load Intercode Contract structure");
  427. break;
  428. }
  429. // Now send the read command for contracts
  430. for(size_t i = 1; i < 5; i++) {
  431. error = read_new_file(
  432. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  433. if(error != 0) {
  434. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  435. break;
  436. }
  437. // Check the response after reading the file
  438. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  439. FURI_LOG_E(
  440. TAG, "Failed to check response after reading contract %d", i);
  441. break;
  442. }
  443. char bit_representation[response_length * 8 + 1];
  444. bit_representation[0] = '\0';
  445. for(size_t i = 0; i < response_length; i++) {
  446. char bits[9];
  447. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  448. byte_to_binary(byte, bits);
  449. strlcat(bit_representation, bits, sizeof(bit_representation));
  450. }
  451. bit_representation[response_length * 8] = '\0';
  452. if(bit_slice_to_dec(
  453. bit_representation,
  454. 0,
  455. IntercodeContractStructure->container->elements[0].bitmap->size -
  456. 1) == 0) {
  457. break;
  458. }
  459. card->navigo->contracts[i - 1].present = 1;
  460. card->contracts_count++;
  461. // 2. ContractTariff
  462. const char* contract_key = "ContractTariff";
  463. if(is_calypso_node_present(
  464. bit_representation, contract_key, IntercodeContractStructure)) {
  465. int positionOffset = get_calypso_node_offset(
  466. bit_representation, contract_key, IntercodeContractStructure);
  467. int start = positionOffset,
  468. end = positionOffset +
  469. get_calypso_node_size(
  470. contract_key, IntercodeContractStructure) -
  471. 1;
  472. card->navigo->contracts[i - 1].tariff =
  473. bit_slice_to_dec(bit_representation, start, end);
  474. }
  475. // 3. ContractSerialNumber
  476. contract_key = "ContractSerialNumber";
  477. if(is_calypso_node_present(
  478. bit_representation, contract_key, IntercodeContractStructure)) {
  479. int positionOffset = get_calypso_node_offset(
  480. bit_representation, contract_key, IntercodeContractStructure);
  481. int start = positionOffset,
  482. end = positionOffset +
  483. get_calypso_node_size(
  484. contract_key, IntercodeContractStructure) -
  485. 1;
  486. card->navigo->contracts[i - 1].serial_number =
  487. bit_slice_to_dec(bit_representation, start, end);
  488. card->navigo->contracts[i - 1].serial_number_available = true;
  489. }
  490. // 8. ContractPayMethod
  491. contract_key = "ContractPayMethod";
  492. if(is_calypso_node_present(
  493. bit_representation, contract_key, IntercodeContractStructure)) {
  494. int positionOffset = get_calypso_node_offset(
  495. bit_representation, contract_key, IntercodeContractStructure);
  496. int start = positionOffset,
  497. end = positionOffset +
  498. get_calypso_node_size(
  499. contract_key, IntercodeContractStructure) -
  500. 1;
  501. card->navigo->contracts[i - 1].pay_method =
  502. bit_slice_to_dec(bit_representation, start, end);
  503. card->navigo->contracts[i - 1].pay_method_available = true;
  504. }
  505. // 10. ContractPriceAmount
  506. contract_key = "ContractPriceAmount";
  507. if(is_calypso_node_present(
  508. bit_representation, contract_key, IntercodeContractStructure)) {
  509. int positionOffset = get_calypso_node_offset(
  510. bit_representation, contract_key, IntercodeContractStructure);
  511. int start = positionOffset,
  512. end = positionOffset +
  513. get_calypso_node_size(
  514. contract_key, IntercodeContractStructure) -
  515. 1;
  516. card->navigo->contracts[i - 1].price_amount =
  517. bit_slice_to_dec(bit_representation, start, end) / 100.0;
  518. card->navigo->contracts[i - 1].price_amount_available = true;
  519. }
  520. // 13.0. ContractValidityStartDate
  521. contract_key = "ContractValidityStartDate";
  522. if(is_calypso_node_present(
  523. bit_representation, contract_key, IntercodeContractStructure)) {
  524. int positionOffset = get_calypso_node_offset(
  525. bit_representation, contract_key, IntercodeContractStructure);
  526. int start = positionOffset,
  527. end = positionOffset +
  528. get_calypso_node_size(
  529. contract_key, IntercodeContractStructure) -
  530. 1;
  531. float decimal_value =
  532. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  533. uint64_t start_validity_timestamp =
  534. (decimal_value + (float)epoch) + 3600;
  535. datetime_timestamp_to_datetime(
  536. start_validity_timestamp,
  537. &card->navigo->contracts[i - 1].start_date);
  538. }
  539. // 13.2. ContractValidityEndDate
  540. contract_key = "ContractValidityEndDate";
  541. if(is_calypso_node_present(
  542. bit_representation, contract_key, IntercodeContractStructure)) {
  543. int positionOffset = get_calypso_node_offset(
  544. bit_representation, contract_key, IntercodeContractStructure);
  545. int start = positionOffset,
  546. end = positionOffset +
  547. get_calypso_node_size(
  548. contract_key, IntercodeContractStructure) -
  549. 1;
  550. float decimal_value =
  551. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  552. uint64_t end_validity_timestamp =
  553. (decimal_value + (float)epoch) + 3600;
  554. datetime_timestamp_to_datetime(
  555. end_validity_timestamp, &card->navigo->contracts[i - 1].end_date);
  556. card->navigo->contracts[i - 1].end_date_available = true;
  557. }
  558. // 13.6. ContractValidityZones
  559. contract_key = "ContractValidityZones";
  560. if(is_calypso_node_present(
  561. bit_representation, contract_key, IntercodeContractStructure)) {
  562. int start = get_calypso_node_offset(
  563. bit_representation, contract_key, IntercodeContractStructure);
  564. // binary form is 00011111 for zones 5, 4, 3, 2, 1
  565. for(int j = 0; j < 5; j++) {
  566. card->navigo->contracts[i - 1].zones[j] = bit_slice_to_dec(
  567. bit_representation, start + 3 + j, start + 3 + j);
  568. }
  569. card->navigo->contracts[i - 1].zones_available = true;
  570. }
  571. // 13.7. ContractValidityJourneys -- pas sûr de le mettre lui
  572. // 15.0. ContractValiditySaleDate
  573. contract_key = "ContractValiditySaleDate";
  574. if(is_calypso_node_present(
  575. bit_representation, contract_key, IntercodeContractStructure)) {
  576. int positionOffset = get_calypso_node_offset(
  577. bit_representation, contract_key, IntercodeContractStructure);
  578. int start = positionOffset,
  579. end = positionOffset +
  580. get_calypso_node_size(
  581. contract_key, IntercodeContractStructure) -
  582. 1;
  583. float decimal_value =
  584. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  585. uint64_t sale_timestamp = (decimal_value + (float)epoch) + 3600;
  586. datetime_timestamp_to_datetime(
  587. sale_timestamp, &card->navigo->contracts[i - 1].sale_date);
  588. }
  589. // 15.2. ContractValiditySaleAgent - FIX NEEDED
  590. contract_key = "ContractValiditySaleAgent";
  591. /* if(is_calypso_node_present(
  592. bit_representation, contract_key, NavigoContractStructure)) { */
  593. int positionOffset = get_calypso_node_offset(
  594. bit_representation, contract_key, IntercodeContractStructure);
  595. int start = positionOffset,
  596. end = positionOffset +
  597. get_calypso_node_size(contract_key, IntercodeContractStructure) -
  598. 1;
  599. card->navigo->contracts[i - 1].sale_agent =
  600. bit_slice_to_dec(bit_representation, start, end);
  601. // }
  602. // 15.3. ContractValiditySaleDevice
  603. contract_key = "ContractValiditySaleDevice";
  604. if(is_calypso_node_present(
  605. bit_representation, contract_key, IntercodeContractStructure)) {
  606. int positionOffset = get_calypso_node_offset(
  607. bit_representation, contract_key, IntercodeContractStructure);
  608. int start = positionOffset,
  609. end = positionOffset +
  610. get_calypso_node_size(
  611. contract_key, IntercodeContractStructure) -
  612. 1;
  613. card->navigo->contracts[i - 1].sale_device =
  614. bit_slice_to_dec(bit_representation, start, end);
  615. }
  616. // 16. ContractStatus -- 0x1 ou 0xff
  617. contract_key = "ContractStatus";
  618. if(is_calypso_node_present(
  619. bit_representation, contract_key, IntercodeContractStructure)) {
  620. int positionOffset = get_calypso_node_offset(
  621. bit_representation, contract_key, IntercodeContractStructure);
  622. int start = positionOffset,
  623. end = positionOffset +
  624. get_calypso_node_size(
  625. contract_key, IntercodeContractStructure) -
  626. 1;
  627. card->navigo->contracts[i - 1].status =
  628. bit_slice_to_dec(bit_representation, start, end);
  629. }
  630. // 18. ContractAuthenticator
  631. contract_key = "ContractAuthenticator";
  632. if(is_calypso_node_present(
  633. bit_representation, contract_key, IntercodeContractStructure)) {
  634. int positionOffset = get_calypso_node_offset(
  635. bit_representation, contract_key, IntercodeContractStructure);
  636. int start = positionOffset,
  637. end = positionOffset +
  638. get_calypso_node_size(
  639. contract_key, IntercodeContractStructure) -
  640. 1;
  641. card->navigo->contracts[i - 1].authenticator =
  642. bit_slice_to_dec(bit_representation, start, end);
  643. }
  644. }
  645. // Free the calypso structure
  646. free_calypso_structure(IntercodeContractStructure);
  647. // Select app for counters (remaining tickets on Navigo Easy)
  648. error = select_new_app(
  649. 0x20, 0x69, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  650. if(error != 0) {
  651. break;
  652. }
  653. // Check the response after selecting app
  654. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  655. break;
  656. }
  657. // read file 1
  658. error =
  659. read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  660. if(error != 0) {
  661. break;
  662. }
  663. // Check the response after reading the file
  664. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  665. break;
  666. }
  667. char counter_bit_representation[response_length * 8 + 1];
  668. counter_bit_representation[0] = '\0';
  669. for(size_t i = 0; i < response_length; i++) {
  670. char bits[9];
  671. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  672. byte_to_binary(byte, bits);
  673. strlcat(
  674. counter_bit_representation, bits, sizeof(counter_bit_representation));
  675. }
  676. // FURI_LOG_I(TAG, "Counter bit_representation: %s", counter_bit_representation);
  677. // Ticket counts (contracts 1-4)
  678. for(int i = 0; i < 4; i++) {
  679. start = 0;
  680. end = 5;
  681. card->navigo->contracts[i].counter.count = bit_slice_to_dec(
  682. counter_bit_representation, 24 * i + start, 24 * i + end);
  683. start = 6;
  684. end = 23;
  685. card->navigo->contracts[i].counter.relative_first_stamp_15mn =
  686. bit_slice_to_dec(
  687. counter_bit_representation, 24 * i + start, 24 * i + end);
  688. }
  689. // Select app for events
  690. error = select_new_app(
  691. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  692. if(error != 0) {
  693. break;
  694. }
  695. // Check the response after selecting app
  696. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  697. break;
  698. }
  699. // Load the calypso structure for events
  700. CalypsoApp* IntercodeEventStructure = get_intercode_event_structure();
  701. if(!IntercodeEventStructure) {
  702. FURI_LOG_E(TAG, "Failed to load Intercode Event structure");
  703. break;
  704. }
  705. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  706. // Now send the read command for events
  707. for(size_t i = 1; i < 4; i++) {
  708. error = read_new_file(
  709. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  710. if(error != 0) {
  711. break;
  712. }
  713. // Check the response after reading the file
  714. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  715. break;
  716. }
  717. char event_bit_representation[response_length * 8 + 1];
  718. event_bit_representation[0] = '\0';
  719. for(size_t i = 0; i < response_length; i++) {
  720. char bits[9];
  721. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  722. byte_to_binary(byte, bits);
  723. strlcat(
  724. event_bit_representation, bits, sizeof(event_bit_representation));
  725. }
  726. // furi_string_cat_printf(parsed_data, "Event 0%d :\n", i);
  727. /* int count = 0;
  728. int start = 25, end = 52;
  729. char bit_slice[end - start + 2];
  730. strncpy(bit_slice, event_bit_representation + start, end - start + 1);
  731. bit_slice[end - start + 1] = '\0';
  732. int* positions = get_bit_positions(bit_slice, &count);
  733. FURI_LOG_I(TAG, "Positions: ");
  734. for(int i = 0; i < count; i++) {
  735. FURI_LOG_I(TAG, "%d ", positions[i]);
  736. } */
  737. // 2. EventCode
  738. const char* event_key = "EventCode";
  739. if(is_calypso_node_present(
  740. event_bit_representation, event_key, IntercodeEventStructure)) {
  741. int positionOffset = get_calypso_node_offset(
  742. event_bit_representation, event_key, IntercodeEventStructure);
  743. int start = positionOffset,
  744. end = positionOffset +
  745. get_calypso_node_size(event_key, IntercodeEventStructure) -
  746. 1;
  747. int decimal_value =
  748. bit_slice_to_dec(event_bit_representation, start, end);
  749. card->navigo->events[i - 1].transport_type = decimal_value >> 4;
  750. card->navigo->events[i - 1].transition = decimal_value & 15;
  751. }
  752. // 4. EventServiceProvider
  753. event_key = "EventServiceProvider";
  754. if(is_calypso_node_present(
  755. event_bit_representation, event_key, IntercodeEventStructure)) {
  756. int positionOffset = get_calypso_node_offset(
  757. event_bit_representation, event_key, IntercodeEventStructure);
  758. start = positionOffset,
  759. end = positionOffset +
  760. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  761. card->navigo->events[i - 1].service_provider =
  762. bit_slice_to_dec(event_bit_representation, start, end);
  763. }
  764. // 8. EventLocationId
  765. event_key = "EventLocationId";
  766. if(is_calypso_node_present(
  767. event_bit_representation, event_key, IntercodeEventStructure)) {
  768. int positionOffset = get_calypso_node_offset(
  769. event_bit_representation, event_key, IntercodeEventStructure);
  770. start = positionOffset,
  771. end = positionOffset +
  772. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  773. int decimal_value =
  774. bit_slice_to_dec(event_bit_representation, start, end);
  775. card->navigo->events[i - 1].station_group_id = decimal_value >> 9;
  776. card->navigo->events[i - 1].station_id = (decimal_value >> 4) & 31;
  777. }
  778. // 9. EventLocationGate
  779. event_key = "EventLocationGate";
  780. if(is_calypso_node_present(
  781. event_bit_representation, event_key, IntercodeEventStructure)) {
  782. int positionOffset = get_calypso_node_offset(
  783. event_bit_representation, event_key, IntercodeEventStructure);
  784. start = positionOffset,
  785. end = positionOffset +
  786. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  787. card->navigo->events[i - 1].location_gate =
  788. bit_slice_to_dec(event_bit_representation, start, end);
  789. card->navigo->events[i - 1].location_gate_available = true;
  790. }
  791. // 10. EventDevice
  792. event_key = "EventDevice";
  793. if(is_calypso_node_present(
  794. event_bit_representation, event_key, IntercodeEventStructure)) {
  795. int positionOffset = get_calypso_node_offset(
  796. event_bit_representation, event_key, IntercodeEventStructure);
  797. start = positionOffset,
  798. end = positionOffset +
  799. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  800. int decimal_value =
  801. bit_slice_to_dec(event_bit_representation, start, end);
  802. card->navigo->events[i - 1].device = decimal_value;
  803. int bus_device = decimal_value >> 8;
  804. card->navigo->events[i - 1].door = bus_device / 2 + 1;
  805. card->navigo->events[i - 1].side = bus_device % 2;
  806. card->navigo->events[i - 1].device_available = true;
  807. }
  808. // 11. EventRouteNumber
  809. event_key = "EventRouteNumber";
  810. if(is_calypso_node_present(
  811. event_bit_representation, event_key, IntercodeEventStructure)) {
  812. int positionOffset = get_calypso_node_offset(
  813. event_bit_representation, event_key, IntercodeEventStructure);
  814. start = positionOffset,
  815. end = positionOffset +
  816. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  817. card->navigo->events[i - 1].route_number =
  818. bit_slice_to_dec(event_bit_representation, start, end);
  819. card->navigo->events[i - 1].route_number_available = true;
  820. }
  821. // 13. EventJourneyRun
  822. event_key = "EventJourneyRun";
  823. if(is_calypso_node_present(
  824. event_bit_representation, event_key, IntercodeEventStructure)) {
  825. int positionOffset = get_calypso_node_offset(
  826. event_bit_representation, event_key, IntercodeEventStructure);
  827. start = positionOffset,
  828. end = positionOffset +
  829. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  830. card->navigo->events[i - 1].mission =
  831. bit_slice_to_dec(event_bit_representation, start, end);
  832. card->navigo->events[i - 1].mission_available = true;
  833. }
  834. // 14. EventVehicleId
  835. event_key = "EventVehicleId";
  836. if(is_calypso_node_present(
  837. event_bit_representation, event_key, IntercodeEventStructure)) {
  838. int positionOffset = get_calypso_node_offset(
  839. event_bit_representation, event_key, IntercodeEventStructure);
  840. start = positionOffset,
  841. end = positionOffset +
  842. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  843. card->navigo->events[i - 1].vehicle_id =
  844. bit_slice_to_dec(event_bit_representation, start, end);
  845. card->navigo->events[i - 1].vehicle_id_available = true;
  846. }
  847. // 25. EventContractPointer
  848. event_key = "EventContractPointer";
  849. if(is_calypso_node_present(
  850. event_bit_representation, event_key, IntercodeEventStructure)) {
  851. int positionOffset = get_calypso_node_offset(
  852. event_bit_representation, event_key, IntercodeEventStructure);
  853. start = positionOffset,
  854. end = positionOffset +
  855. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  856. card->navigo->events[i - 1].used_contract =
  857. bit_slice_to_dec(event_bit_representation, start, end);
  858. card->navigo->events[i - 1].used_contract_available = true;
  859. }
  860. // EventDateStamp
  861. event_key = "EventDateStamp";
  862. int positionOffset = get_calypso_node_offset(
  863. event_bit_representation, event_key, IntercodeEventStructure);
  864. start = positionOffset,
  865. end = positionOffset +
  866. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  867. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  868. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  869. datetime_timestamp_to_datetime(
  870. date_timestamp, &card->navigo->events[i - 1].date);
  871. // EventTimeStamp
  872. event_key = "EventTimeStamp";
  873. positionOffset = get_calypso_node_offset(
  874. event_bit_representation, event_key, IntercodeEventStructure);
  875. start = positionOffset,
  876. end = positionOffset +
  877. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  878. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  879. card->navigo->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  880. card->navigo->events[i - 1].date.minute =
  881. ((decimal_value * 60) % 3600) / 60;
  882. card->navigo->events[i - 1].date.second =
  883. ((decimal_value * 60) % 3600) % 60;
  884. }
  885. // Free the calypso structure
  886. free_calypso_structure(IntercodeEventStructure);
  887. break;
  888. }
  889. case CALYPSO_CARD_OPUS: {
  890. card->opus = malloc(sizeof(OpusCardData));
  891. card->opus->environment.country_num = country_num;
  892. card->opus->environment.network_num = network_num;
  893. CalypsoApp* OpusEnvHolderStructure = get_opus_env_holder_structure();
  894. // EnvApplicationVersionNumber
  895. const char* env_key = "EnvApplicationVersionNumber";
  896. int positionOffset = get_calypso_node_offset(
  897. environment_bit_representation, env_key, OpusEnvHolderStructure);
  898. int start = positionOffset,
  899. end = positionOffset +
  900. get_calypso_node_size(env_key, OpusEnvHolderStructure) - 1;
  901. card->opus->environment.app_version =
  902. bit_slice_to_dec(environment_bit_representation, start, end);
  903. // EnvApplicationValidityEndDate
  904. env_key = "EnvApplicationValidityEndDate";
  905. positionOffset = get_calypso_node_offset(
  906. environment_bit_representation, env_key, OpusEnvHolderStructure);
  907. start = positionOffset,
  908. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  909. 1;
  910. float decimal_value =
  911. bit_slice_to_dec(environment_bit_representation, start, end);
  912. uint64_t end_validity_timestamp =
  913. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  914. datetime_timestamp_to_datetime(
  915. end_validity_timestamp, &card->opus->environment.end_dt);
  916. // HolderDataCardStatus
  917. env_key = "HolderDataCardStatus";
  918. positionOffset = get_calypso_node_offset(
  919. environment_bit_representation, env_key, OpusEnvHolderStructure);
  920. start = positionOffset,
  921. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  922. 1;
  923. card->opus->holder.card_status =
  924. bit_slice_to_dec(environment_bit_representation, start, end);
  925. // HolderDataCommercialID
  926. env_key = "HolderDataCommercialID";
  927. positionOffset = get_calypso_node_offset(
  928. environment_bit_representation, env_key, OpusEnvHolderStructure);
  929. start = positionOffset,
  930. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  931. 1;
  932. card->opus->holder.commercial_id =
  933. bit_slice_to_dec(environment_bit_representation, start, end);
  934. // Free the calypso structure
  935. free_calypso_structure(OpusEnvHolderStructure);
  936. // Select app for contracts
  937. error = select_new_app(
  938. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  939. if(error != 0) {
  940. FURI_LOG_E(TAG, "Failed to select app for contracts");
  941. break;
  942. }
  943. // Check the response after selecting app
  944. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  945. FURI_LOG_E(
  946. TAG, "Failed to check response after selecting app for contracts");
  947. break;
  948. }
  949. // Prepare calypso structure
  950. CalypsoApp* OpusContractStructure = get_opus_contract_structure();
  951. if(!OpusContractStructure) {
  952. FURI_LOG_E(TAG, "Failed to load Opus Contract structure");
  953. break;
  954. }
  955. // Now send the read command for contracts
  956. for(size_t i = 1; i < 5; i++) {
  957. error = read_new_file(
  958. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  959. if(error != 0) {
  960. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  961. break;
  962. }
  963. // Check the response after reading the file
  964. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  965. FURI_LOG_E(
  966. TAG, "Failed to check response after reading contract %d", i);
  967. break;
  968. }
  969. char bit_representation[response_length * 8 + 1];
  970. bit_representation[0] = '\0';
  971. for(size_t i = 0; i < response_length; i++) {
  972. char bits[9];
  973. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  974. byte_to_binary(byte, bits);
  975. strlcat(bit_representation, bits, sizeof(bit_representation));
  976. }
  977. bit_representation[response_length * 8] = '\0';
  978. if(bit_slice_to_dec(
  979. bit_representation,
  980. 0,
  981. OpusContractStructure->container->elements[1].bitmap->size - 1) ==
  982. 0) {
  983. break;
  984. }
  985. card->opus->contracts[i - 1].present = 1;
  986. card->contracts_count++;
  987. // ContractProvider
  988. const char* contract_key = "ContractProvider";
  989. if(is_calypso_node_present(
  990. bit_representation, contract_key, OpusContractStructure)) {
  991. int positionOffset = get_calypso_node_offset(
  992. bit_representation, contract_key, OpusContractStructure);
  993. int start = positionOffset,
  994. end = positionOffset +
  995. get_calypso_node_size(contract_key, OpusContractStructure) -
  996. 1;
  997. card->opus->contracts[i - 1].provider =
  998. bit_slice_to_dec(bit_representation, start, end);
  999. }
  1000. // ContractTariff
  1001. contract_key = "ContractTariff";
  1002. if(is_calypso_node_present(
  1003. bit_representation, contract_key, OpusContractStructure)) {
  1004. int positionOffset = get_calypso_node_offset(
  1005. bit_representation, contract_key, OpusContractStructure);
  1006. int start = positionOffset,
  1007. end = positionOffset +
  1008. get_calypso_node_size(contract_key, OpusContractStructure) -
  1009. 1;
  1010. card->opus->contracts[i - 1].tariff =
  1011. bit_slice_to_dec(bit_representation, start, end);
  1012. }
  1013. // ContractStartDate
  1014. contract_key = "ContractStartDate";
  1015. if(is_calypso_node_present(
  1016. bit_representation, contract_key, OpusContractStructure)) {
  1017. int positionOffset = get_calypso_node_offset(
  1018. bit_representation, contract_key, OpusContractStructure);
  1019. int start = positionOffset,
  1020. end = positionOffset +
  1021. get_calypso_node_size(contract_key, OpusContractStructure) -
  1022. 1;
  1023. float decimal_value =
  1024. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1025. uint64_t start_validity_timestamp =
  1026. (decimal_value + (float)epoch) + 3600;
  1027. datetime_timestamp_to_datetime(
  1028. start_validity_timestamp,
  1029. &card->opus->contracts[i - 1].start_date);
  1030. }
  1031. // ContractEndDate
  1032. contract_key = "ContractEndDate";
  1033. if(is_calypso_node_present(
  1034. bit_representation, contract_key, OpusContractStructure)) {
  1035. int positionOffset = get_calypso_node_offset(
  1036. bit_representation, contract_key, OpusContractStructure);
  1037. int start = positionOffset,
  1038. end = positionOffset +
  1039. get_calypso_node_size(contract_key, OpusContractStructure) -
  1040. 1;
  1041. float decimal_value =
  1042. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1043. uint64_t end_validity_timestamp =
  1044. (decimal_value + (float)epoch) + 3600;
  1045. datetime_timestamp_to_datetime(
  1046. end_validity_timestamp, &card->opus->contracts[i - 1].end_date);
  1047. }
  1048. // ContractStatus
  1049. contract_key = "ContractStatus";
  1050. if(is_calypso_node_present(
  1051. bit_representation, contract_key, OpusContractStructure)) {
  1052. int positionOffset = get_calypso_node_offset(
  1053. bit_representation, contract_key, OpusContractStructure);
  1054. int start = positionOffset,
  1055. end = positionOffset +
  1056. get_calypso_node_size(contract_key, OpusContractStructure) -
  1057. 1;
  1058. card->opus->contracts[i - 1].status =
  1059. bit_slice_to_dec(bit_representation, start, end);
  1060. }
  1061. // ContractSaleDate + ContractSaleTime
  1062. contract_key = "ContractSaleDate";
  1063. int positionOffset = get_calypso_node_offset(
  1064. bit_representation, contract_key, OpusContractStructure);
  1065. int start = positionOffset,
  1066. end = positionOffset +
  1067. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1068. uint64_t sale_date_timestamp =
  1069. (bit_slice_to_dec(bit_representation, start, end) + (float)epoch) +
  1070. 3600;
  1071. datetime_timestamp_to_datetime(
  1072. sale_date_timestamp, &card->opus->contracts[i - 1].sale_date);
  1073. contract_key = "ContractSaleTime";
  1074. positionOffset = get_calypso_node_offset(
  1075. bit_representation, contract_key, OpusContractStructure);
  1076. start = positionOffset,
  1077. end = positionOffset +
  1078. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1079. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  1080. card->opus->contracts[i - 1].sale_date.hour = (decimal_value * 60) / 3600;
  1081. card->opus->contracts[i - 1].sale_date.minute =
  1082. ((decimal_value * 60) % 3600) / 60;
  1083. card->opus->contracts[i - 1].sale_date.second =
  1084. ((decimal_value * 60) % 3600) % 60;
  1085. }
  1086. // Free the calypso structure
  1087. free_calypso_structure(OpusContractStructure);
  1088. // Select app for events
  1089. error = select_new_app(
  1090. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1091. if(error != 0) {
  1092. break;
  1093. }
  1094. // Check the response after selecting app
  1095. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1096. break;
  1097. }
  1098. // Load the calypso structure for events
  1099. CalypsoApp* OpusEventStructure = get_opus_event_structure();
  1100. if(!OpusEventStructure) {
  1101. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1102. break;
  1103. }
  1104. // Now send the read command for events
  1105. for(size_t i = 1; i < 4; i++) {
  1106. error = read_new_file(
  1107. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1108. if(error != 0) {
  1109. break;
  1110. }
  1111. // Check the response after reading the file
  1112. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1113. break;
  1114. }
  1115. char event_bit_representation[response_length * 8 + 1];
  1116. event_bit_representation[0] = '\0';
  1117. for(size_t i = 0; i < response_length; i++) {
  1118. char bits[9];
  1119. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1120. byte_to_binary(byte, bits);
  1121. strlcat(
  1122. event_bit_representation, bits, sizeof(event_bit_representation));
  1123. }
  1124. // EventServiceProvider
  1125. const char* event_key = "EventServiceProvider";
  1126. if(is_calypso_node_present(
  1127. event_bit_representation, event_key, OpusEventStructure)) {
  1128. int positionOffset = get_calypso_node_offset(
  1129. event_bit_representation, event_key, OpusEventStructure);
  1130. int start = positionOffset,
  1131. end = positionOffset +
  1132. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1133. card->opus->events[i - 1].service_provider =
  1134. bit_slice_to_dec(event_bit_representation, start, end);
  1135. }
  1136. // EventRouteNumber
  1137. event_key = "EventRouteNumber";
  1138. if(is_calypso_node_present(
  1139. event_bit_representation, event_key, OpusEventStructure)) {
  1140. int positionOffset = get_calypso_node_offset(
  1141. event_bit_representation, event_key, OpusEventStructure);
  1142. int start = positionOffset,
  1143. end = positionOffset +
  1144. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1145. card->opus->events[i - 1].route_number =
  1146. bit_slice_to_dec(event_bit_representation, start, end);
  1147. card->opus->events[i - 1].route_number_available = true;
  1148. }
  1149. // EventContractPointer
  1150. event_key = "EventContractPointer";
  1151. if(is_calypso_node_present(
  1152. event_bit_representation, event_key, OpusEventStructure)) {
  1153. int positionOffset = get_calypso_node_offset(
  1154. event_bit_representation, event_key, OpusEventStructure);
  1155. int start = positionOffset,
  1156. end = positionOffset +
  1157. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1158. card->opus->events[i - 1].used_contract =
  1159. bit_slice_to_dec(event_bit_representation, start, end);
  1160. card->opus->events[i - 1].used_contract_available = true;
  1161. }
  1162. // EventDate + EventTime
  1163. event_key = "EventDate";
  1164. int positionOffset = get_calypso_node_offset(
  1165. event_bit_representation, event_key, OpusEventStructure);
  1166. int start = positionOffset,
  1167. end = positionOffset +
  1168. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1169. uint64_t date_timestamp =
  1170. (bit_slice_to_dec(event_bit_representation, start, end) +
  1171. (float)epoch) +
  1172. 3600;
  1173. datetime_timestamp_to_datetime(
  1174. date_timestamp, &card->opus->events[i - 1].date);
  1175. event_key = "EventTime";
  1176. positionOffset = get_calypso_node_offset(
  1177. event_bit_representation, event_key, OpusEventStructure);
  1178. start = positionOffset,
  1179. end = positionOffset +
  1180. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1181. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1182. card->opus->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1183. card->opus->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  1184. card->opus->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  1185. }
  1186. // Free the calypso structure
  1187. free_calypso_structure(OpusEventStructure);
  1188. break;
  1189. }
  1190. default:
  1191. break;
  1192. }
  1193. widget_add_text_scroll_element(
  1194. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1195. CalypsoContext* context = malloc(sizeof(CalypsoContext));
  1196. context->card = card;
  1197. context->page_id = 0;
  1198. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  1199. app->calypso_context = context;
  1200. // Ensure no nested mutexes
  1201. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1202. update_page_info(app, parsed_data);
  1203. furi_mutex_release(context->mutex);
  1204. widget_add_text_scroll_element(
  1205. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1206. // Ensure no nested mutexes
  1207. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1208. update_widget_elements(app);
  1209. furi_mutex_release(context->mutex);
  1210. furi_string_free(parsed_data);
  1211. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  1212. metroflip_app_blink_stop(app);
  1213. stage = MetroflipPollerEventTypeSuccess;
  1214. next_command = NfcCommandStop;
  1215. } while(false);
  1216. if(stage != MetroflipPollerEventTypeSuccess) {
  1217. next_command = NfcCommandStop;
  1218. }
  1219. }
  1220. }
  1221. bit_buffer_free(tx_buffer);
  1222. bit_buffer_free(rx_buffer);
  1223. return next_command;
  1224. }
  1225. void metroflip_scene_navigo_on_enter(void* context) {
  1226. Metroflip* app = context;
  1227. dolphin_deed(DolphinDeedNfcRead);
  1228. // Setup view
  1229. Popup* popup = app->popup;
  1230. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  1231. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  1232. // Start worker
  1233. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  1234. nfc_scanner_alloc(app->nfc);
  1235. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  1236. nfc_poller_start(app->poller, metroflip_scene_navigo_poller_callback, app);
  1237. metroflip_app_blink_start(app);
  1238. }
  1239. bool metroflip_scene_navigo_on_event(void* context, SceneManagerEvent event) {
  1240. Metroflip* app = context;
  1241. bool consumed = false;
  1242. if(event.type == SceneManagerEventTypeCustom) {
  1243. if(event.event == MetroflipPollerEventTypeCardDetect) {
  1244. Popup* popup = app->popup;
  1245. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  1246. consumed = true;
  1247. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  1248. Popup* popup = app->popup;
  1249. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  1250. consumed = true;
  1251. } else if(event.event == MetroflipCustomEventPollerFail) {
  1252. Popup* popup = app->popup;
  1253. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  1254. consumed = true;
  1255. }
  1256. } else if(event.type == SceneManagerEventTypeBack) {
  1257. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  1258. consumed = true;
  1259. }
  1260. return consumed;
  1261. }
  1262. void metroflip_scene_navigo_on_exit(void* context) {
  1263. Metroflip* app = context;
  1264. if(app->poller) {
  1265. nfc_poller_stop(app->poller);
  1266. nfc_poller_free(app->poller);
  1267. }
  1268. metroflip_app_blink_stop(app);
  1269. widget_reset(app->widget);
  1270. // Clear view
  1271. popup_reset(app->popup);
  1272. if(app->calypso_context) {
  1273. CalypsoContext* ctx = app->calypso_context;
  1274. free(ctx->card->navigo);
  1275. free(ctx->card->opus);
  1276. free(ctx->card);
  1277. furi_mutex_free(ctx->mutex);
  1278. free(ctx);
  1279. app->calypso_context = NULL;
  1280. }
  1281. }