metroflip_scene_calypso.c 72 KB

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