metroflip_scene_calypso.c 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110
  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. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  97. furi_string_cat_printf(
  98. parsed_data,
  99. "\e#%s %u:\n",
  100. get_network_string(ctx->card->card_type),
  101. ctx->card->card_number);
  102. return;
  103. }
  104. if(ctx->page_id <= 3) {
  105. switch(ctx->card->card_type) {
  106. case CALYPSO_CARD_NAVIGO: {
  107. furi_string_cat_printf(
  108. parsed_data,
  109. "\e#%s %u:\n",
  110. get_navigo_type(ctx->card->navigo->holder.card_status),
  111. ctx->card->card_number);
  112. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  113. show_navigo_contract_info(&ctx->card->navigo->contracts[ctx->page_id], parsed_data);
  114. break;
  115. }
  116. case CALYPSO_CARD_OPUS: {
  117. furi_string_cat_printf(parsed_data, "\e#Opus %u:\n", ctx->card->card_number);
  118. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  119. show_opus_contract_info(&ctx->card->opus->contracts[ctx->page_id], parsed_data);
  120. break;
  121. }
  122. case CALYPSO_CARD_RAVKAV: {
  123. if(ctx->card->card_number == 0) {
  124. furi_string_cat_printf(parsed_data, "\e#Anonymous Rav-Kav:\n");
  125. } else {
  126. furi_string_cat_printf(parsed_data, "\e#RavKav %u:\n", ctx->card->card_number);
  127. }
  128. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  129. show_ravkav_contract_info(&ctx->card->ravkav->contracts[ctx->page_id], parsed_data);
  130. break;
  131. }
  132. default: {
  133. furi_string_cat_printf(parsed_data, "\e#Unknown %u:\n", ctx->card->card_number);
  134. break;
  135. }
  136. }
  137. } else if(ctx->page_id == 4) {
  138. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  139. switch(ctx->card->card_type) {
  140. case CALYPSO_CARD_NAVIGO: {
  141. show_navigo_environment_info(&ctx->card->navigo->environment, parsed_data);
  142. break;
  143. }
  144. case CALYPSO_CARD_OPUS: {
  145. show_opus_environment_info(&ctx->card->opus->environment, parsed_data);
  146. break;
  147. }
  148. case CALYPSO_CARD_RAVKAV: {
  149. show_ravkav_environment_info(&ctx->card->ravkav->environment, parsed_data);
  150. break;
  151. }
  152. default: {
  153. break;
  154. }
  155. }
  156. } else if(ctx->page_id >= 5) {
  157. furi_string_cat_printf(parsed_data, "\e#Event %d:\n", ctx->page_id - 4);
  158. switch(ctx->card->card_type) {
  159. case CALYPSO_CARD_NAVIGO: {
  160. show_navigo_event_info(
  161. &ctx->card->navigo->events[ctx->page_id - 5],
  162. ctx->card->navigo->contracts,
  163. parsed_data);
  164. break;
  165. }
  166. case CALYPSO_CARD_OPUS: {
  167. show_opus_event_info(
  168. &ctx->card->opus->events[ctx->page_id - 5],
  169. ctx->card->opus->contracts,
  170. parsed_data);
  171. break;
  172. }
  173. case CALYPSO_CARD_RAVKAV: {
  174. show_ravkav_event_info(&ctx->card->ravkav->events[ctx->page_id - 5], parsed_data);
  175. break;
  176. }
  177. default: {
  178. break;
  179. }
  180. }
  181. }
  182. }
  183. void update_widget_elements(void* context) {
  184. Metroflip* app = context;
  185. CalypsoContext* ctx = app->calypso_context;
  186. Widget* widget = app->widget;
  187. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO && ctx->card->card_type != CALYPSO_CARD_OPUS &&
  188. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  189. widget_add_button_element(
  190. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  191. return;
  192. }
  193. if(ctx->page_id < 7) {
  194. widget_add_button_element(
  195. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  196. } else {
  197. widget_add_button_element(
  198. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  199. }
  200. if(ctx->page_id > 0) {
  201. widget_add_button_element(
  202. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  203. }
  204. }
  205. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  206. Metroflip* app = context;
  207. CalypsoContext* ctx = app->calypso_context;
  208. UNUSED(result);
  209. Widget* widget = app->widget;
  210. if(type == InputTypePress) {
  211. widget_reset(widget);
  212. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  213. if(ctx->page_id > 0) {
  214. if(ctx->page_id == 4 && ctx->card->contracts_count < 4) {
  215. ctx->page_id -= 1;
  216. }
  217. if(ctx->page_id == 3 && ctx->card->contracts_count < 3) {
  218. ctx->page_id -= 1;
  219. }
  220. if(ctx->page_id == 2 && ctx->card->contracts_count < 2) {
  221. ctx->page_id -= 1;
  222. }
  223. ctx->page_id -= 1;
  224. }
  225. FuriString* parsed_data = furi_string_alloc();
  226. // Ensure no nested mutexes
  227. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  228. update_page_info(app, parsed_data);
  229. furi_mutex_release(ctx->mutex);
  230. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  231. // widget_add_icon_element(widget, 0, 0, &I_RFIDDolphinReceive_97x61);
  232. // Ensure no nested mutexes
  233. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  234. update_widget_elements(app);
  235. furi_mutex_release(ctx->mutex);
  236. furi_string_free(parsed_data);
  237. }
  238. }
  239. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  240. Metroflip* app = context;
  241. CalypsoContext* ctx = app->calypso_context;
  242. UNUSED(result);
  243. Widget* widget = app->widget;
  244. if(type == InputTypePress) {
  245. widget_reset(widget);
  246. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  247. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO &&
  248. ctx->card->card_type != CALYPSO_CARD_OPUS &&
  249. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  250. ctx->page_id = 0;
  251. scene_manager_search_and_switch_to_previous_scene(
  252. app->scene_manager, MetroflipSceneStart);
  253. return;
  254. }
  255. if(ctx->page_id < 7) {
  256. if(ctx->page_id == 0 && ctx->card->contracts_count < 2) {
  257. ctx->page_id += 1;
  258. }
  259. if(ctx->page_id == 1 && ctx->card->contracts_count < 3) {
  260. ctx->page_id += 1;
  261. }
  262. if(ctx->page_id == 2 && ctx->card->contracts_count < 4) {
  263. ctx->page_id += 1;
  264. }
  265. ctx->page_id += 1;
  266. } else {
  267. ctx->page_id = 0;
  268. scene_manager_search_and_switch_to_previous_scene(
  269. app->scene_manager, MetroflipSceneStart);
  270. return;
  271. }
  272. FuriString* parsed_data = furi_string_alloc();
  273. // Ensure no nested mutexes
  274. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  275. update_page_info(app, parsed_data);
  276. furi_mutex_release(ctx->mutex);
  277. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  278. // Ensure no nested mutexes
  279. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  280. update_widget_elements(app);
  281. furi_mutex_release(ctx->mutex);
  282. furi_string_free(parsed_data);
  283. }
  284. }
  285. void delay(int milliseconds) {
  286. furi_thread_flags_wait(0, FuriFlagWaitAny, milliseconds);
  287. }
  288. static NfcCommand metroflip_scene_calypso_poller_callback(NfcGenericEvent event, void* context) {
  289. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  290. NfcCommand next_command = NfcCommandContinue;
  291. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  292. Metroflip* app = context;
  293. FuriString* parsed_data = furi_string_alloc();
  294. Widget* widget = app->widget;
  295. furi_string_reset(app->text_box_store);
  296. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  297. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  298. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  299. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  300. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  301. if(stage == MetroflipPollerEventTypeStart) {
  302. // Start Flipper vibration
  303. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  304. notification_message(notification, &sequence_set_vibro_on);
  305. delay(50);
  306. notification_message(notification, &sequence_reset_vibro);
  307. nfc_device_set_data(
  308. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  309. Iso14443_4bError error;
  310. size_t response_length = 0;
  311. do {
  312. // Initialize the card data
  313. CalypsoCardData* card = malloc(sizeof(CalypsoCardData));
  314. // Select app ICC
  315. error = select_new_app(
  316. 0x00, 0x02, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  317. if(error != 0) {
  318. break;
  319. }
  320. // Check the response after selecting app
  321. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  322. break;
  323. }
  324. // Now send the read command for ICC
  325. error = read_new_file(0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  326. if(error != 0) {
  327. break;
  328. }
  329. // Check the response after reading the file
  330. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  331. break;
  332. }
  333. char icc_bit_representation[response_length * 8 + 1];
  334. icc_bit_representation[0] = '\0';
  335. for(size_t i = 0; i < response_length; i++) {
  336. char bits[9];
  337. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  338. byte_to_binary(byte, bits);
  339. strlcat(icc_bit_representation, bits, sizeof(icc_bit_representation));
  340. }
  341. icc_bit_representation[response_length * 8] = '\0';
  342. int start = 128, end = 159;
  343. card->card_number = bit_slice_to_dec(icc_bit_representation, start, end);
  344. // Select app for ticketing
  345. error = select_new_app(
  346. 0x20, 0x00, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  347. if(error != 0) {
  348. FURI_LOG_E(TAG, "Failed to select app for ticketing");
  349. break;
  350. }
  351. // Check the response after selecting app
  352. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  353. FURI_LOG_E(TAG, "Failed to check response after selecting app for ticketing");
  354. break;
  355. }
  356. // Select app for environment
  357. error = select_new_app(
  358. 0x20, 0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  359. if(error != 0) {
  360. break;
  361. }
  362. // Check the response after selecting app
  363. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  364. break;
  365. }
  366. // read file 1
  367. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  368. if(error != 0) {
  369. break;
  370. }
  371. // Check the response after reading the file
  372. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  373. break;
  374. }
  375. char environment_bit_representation[response_length * 8 + 1];
  376. environment_bit_representation[0] = '\0';
  377. for(size_t i = 0; i < response_length; i++) {
  378. char bits[9];
  379. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  380. byte_to_binary(byte, bits);
  381. strlcat(
  382. environment_bit_representation,
  383. bits,
  384. sizeof(environment_bit_representation));
  385. }
  386. // FURI_LOG_I(
  387. // TAG, "Environment bit_representation: %s", environment_bit_representation);
  388. start = 13;
  389. end = 16;
  390. int country_num =
  391. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  392. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  393. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  394. start = 25;
  395. end = 28;
  396. int network_num =
  397. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  398. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  399. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  400. card->card_type = guess_card_type(country_num, network_num);
  401. switch(card->card_type) {
  402. case CALYPSO_CARD_NAVIGO: {
  403. card->navigo = malloc(sizeof(NavigoCardData));
  404. card->navigo->environment.country_num = country_num;
  405. card->navigo->environment.network_num = network_num;
  406. CalypsoApp* IntercodeEnvHolderStructure = get_intercode_env_holder_structure();
  407. // EnvApplicationVersionNumber
  408. const char* env_key = "EnvApplicationVersionNumber";
  409. int positionOffset = get_calypso_node_offset(
  410. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  411. int start = positionOffset,
  412. end = positionOffset +
  413. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  414. card->navigo->environment.app_version =
  415. bit_slice_to_dec(environment_bit_representation, start, end);
  416. // EnvApplicationValidityEndDate
  417. env_key = "EnvApplicationValidityEndDate";
  418. positionOffset = get_calypso_node_offset(
  419. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  420. start = positionOffset,
  421. end = positionOffset +
  422. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  423. float decimal_value =
  424. bit_slice_to_dec(environment_bit_representation, start, end);
  425. uint64_t end_validity_timestamp =
  426. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  427. datetime_timestamp_to_datetime(
  428. end_validity_timestamp, &card->navigo->environment.end_dt);
  429. // HolderDataCardStatus
  430. env_key = "HolderDataCardStatus";
  431. positionOffset = get_calypso_node_offset(
  432. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  433. start = positionOffset,
  434. end = positionOffset +
  435. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  436. card->navigo->holder.card_status =
  437. bit_slice_to_dec(environment_bit_representation, start, end);
  438. // HolderDataCommercialID
  439. env_key = "HolderDataCommercialID";
  440. positionOffset = get_calypso_node_offset(
  441. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  442. start = positionOffset,
  443. end = positionOffset +
  444. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  445. card->navigo->holder.commercial_id =
  446. bit_slice_to_dec(environment_bit_representation, start, end);
  447. // Free the calypso structure
  448. free_calypso_structure(IntercodeEnvHolderStructure);
  449. // Select app for contracts
  450. error = select_new_app(
  451. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  452. if(error != 0) {
  453. FURI_LOG_E(TAG, "Failed to select app for contracts");
  454. break;
  455. }
  456. // Check the response after selecting app
  457. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  458. FURI_LOG_E(
  459. TAG, "Failed to check response after selecting app for contracts");
  460. break;
  461. }
  462. // Prepare calypso structure
  463. CalypsoApp* IntercodeContractStructure = get_intercode_contract_structure();
  464. if(!IntercodeContractStructure) {
  465. FURI_LOG_E(TAG, "Failed to load Intercode Contract structure");
  466. break;
  467. }
  468. // Now send the read command for contracts
  469. for(size_t i = 1; i < 5; i++) {
  470. error = read_new_file(
  471. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  472. if(error != 0) {
  473. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  474. break;
  475. }
  476. // Check the response after reading the file
  477. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  478. FURI_LOG_E(
  479. TAG, "Failed to check response after reading contract %d", i);
  480. break;
  481. }
  482. char bit_representation[response_length * 8 + 1];
  483. bit_representation[0] = '\0';
  484. for(size_t i = 0; i < response_length; i++) {
  485. char bits[9];
  486. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  487. byte_to_binary(byte, bits);
  488. strlcat(bit_representation, bits, sizeof(bit_representation));
  489. }
  490. bit_representation[response_length * 8] = '\0';
  491. if(bit_slice_to_dec(
  492. bit_representation,
  493. 0,
  494. IntercodeContractStructure->container->elements[0].bitmap->size -
  495. 1) == 0) {
  496. break;
  497. }
  498. card->navigo->contracts[i - 1].present = 1;
  499. card->contracts_count++;
  500. // 2. ContractTariff
  501. const char* contract_key = "ContractTariff";
  502. if(is_calypso_node_present(
  503. bit_representation, contract_key, IntercodeContractStructure)) {
  504. int positionOffset = get_calypso_node_offset(
  505. bit_representation, contract_key, IntercodeContractStructure);
  506. int start = positionOffset,
  507. end = positionOffset +
  508. get_calypso_node_size(
  509. contract_key, IntercodeContractStructure) -
  510. 1;
  511. card->navigo->contracts[i - 1].tariff =
  512. bit_slice_to_dec(bit_representation, start, end);
  513. }
  514. // 3. ContractSerialNumber
  515. contract_key = "ContractSerialNumber";
  516. if(is_calypso_node_present(
  517. bit_representation, contract_key, IntercodeContractStructure)) {
  518. int positionOffset = get_calypso_node_offset(
  519. bit_representation, contract_key, IntercodeContractStructure);
  520. int start = positionOffset,
  521. end = positionOffset +
  522. get_calypso_node_size(
  523. contract_key, IntercodeContractStructure) -
  524. 1;
  525. card->navigo->contracts[i - 1].serial_number =
  526. bit_slice_to_dec(bit_representation, start, end);
  527. card->navigo->contracts[i - 1].serial_number_available = true;
  528. }
  529. // 8. ContractPayMethod
  530. contract_key = "ContractPayMethod";
  531. if(is_calypso_node_present(
  532. bit_representation, contract_key, IntercodeContractStructure)) {
  533. int positionOffset = get_calypso_node_offset(
  534. bit_representation, contract_key, IntercodeContractStructure);
  535. int start = positionOffset,
  536. end = positionOffset +
  537. get_calypso_node_size(
  538. contract_key, IntercodeContractStructure) -
  539. 1;
  540. card->navigo->contracts[i - 1].pay_method =
  541. bit_slice_to_dec(bit_representation, start, end);
  542. card->navigo->contracts[i - 1].pay_method_available = true;
  543. }
  544. // 10. ContractPriceAmount
  545. contract_key = "ContractPriceAmount";
  546. if(is_calypso_node_present(
  547. bit_representation, contract_key, IntercodeContractStructure)) {
  548. int positionOffset = get_calypso_node_offset(
  549. bit_representation, contract_key, IntercodeContractStructure);
  550. int start = positionOffset,
  551. end = positionOffset +
  552. get_calypso_node_size(
  553. contract_key, IntercodeContractStructure) -
  554. 1;
  555. card->navigo->contracts[i - 1].price_amount =
  556. bit_slice_to_dec(bit_representation, start, end) / 100.0;
  557. card->navigo->contracts[i - 1].price_amount_available = true;
  558. }
  559. // 13.0. ContractValidityStartDate
  560. contract_key = "ContractValidityStartDate";
  561. if(is_calypso_node_present(
  562. bit_representation, contract_key, IntercodeContractStructure)) {
  563. int positionOffset = get_calypso_node_offset(
  564. bit_representation, contract_key, IntercodeContractStructure);
  565. int start = positionOffset,
  566. end = positionOffset +
  567. get_calypso_node_size(
  568. contract_key, IntercodeContractStructure) -
  569. 1;
  570. float decimal_value =
  571. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  572. uint64_t start_validity_timestamp =
  573. (decimal_value + (float)epoch) + 3600;
  574. datetime_timestamp_to_datetime(
  575. start_validity_timestamp,
  576. &card->navigo->contracts[i - 1].start_date);
  577. }
  578. // 13.2. ContractValidityEndDate
  579. contract_key = "ContractValidityEndDate";
  580. if(is_calypso_node_present(
  581. bit_representation, contract_key, IntercodeContractStructure)) {
  582. int positionOffset = get_calypso_node_offset(
  583. bit_representation, contract_key, IntercodeContractStructure);
  584. int start = positionOffset,
  585. end = positionOffset +
  586. get_calypso_node_size(
  587. contract_key, IntercodeContractStructure) -
  588. 1;
  589. float decimal_value =
  590. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  591. uint64_t end_validity_timestamp =
  592. (decimal_value + (float)epoch) + 3600;
  593. datetime_timestamp_to_datetime(
  594. end_validity_timestamp, &card->navigo->contracts[i - 1].end_date);
  595. card->navigo->contracts[i - 1].end_date_available = true;
  596. }
  597. // 13.6. ContractValidityZones
  598. contract_key = "ContractValidityZones";
  599. if(is_calypso_node_present(
  600. bit_representation, contract_key, IntercodeContractStructure)) {
  601. int start = get_calypso_node_offset(
  602. bit_representation, contract_key, IntercodeContractStructure);
  603. // binary form is 00011111 for zones 5, 4, 3, 2, 1
  604. for(int j = 0; j < 5; j++) {
  605. card->navigo->contracts[i - 1].zones[j] = bit_slice_to_dec(
  606. bit_representation, start + 3 + j, start + 3 + j);
  607. }
  608. card->navigo->contracts[i - 1].zones_available = true;
  609. }
  610. // 13.7. ContractValidityJourneys -- pas sûr de le mettre lui
  611. // 15.0. ContractValiditySaleDate
  612. contract_key = "ContractValiditySaleDate";
  613. if(is_calypso_node_present(
  614. bit_representation, contract_key, IntercodeContractStructure)) {
  615. int positionOffset = get_calypso_node_offset(
  616. bit_representation, contract_key, IntercodeContractStructure);
  617. int start = positionOffset,
  618. end = positionOffset +
  619. get_calypso_node_size(
  620. contract_key, IntercodeContractStructure) -
  621. 1;
  622. float decimal_value =
  623. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  624. uint64_t sale_timestamp = (decimal_value + (float)epoch) + 3600;
  625. datetime_timestamp_to_datetime(
  626. sale_timestamp, &card->navigo->contracts[i - 1].sale_date);
  627. }
  628. // 15.2. ContractValiditySaleAgent - FIX NEEDED
  629. contract_key = "ContractValiditySaleAgent";
  630. /* if(is_calypso_node_present(
  631. bit_representation, contract_key, NavigoContractStructure)) { */
  632. int positionOffset = get_calypso_node_offset(
  633. bit_representation, contract_key, IntercodeContractStructure);
  634. int start = positionOffset,
  635. end = positionOffset +
  636. get_calypso_node_size(contract_key, IntercodeContractStructure) -
  637. 1;
  638. card->navigo->contracts[i - 1].sale_agent =
  639. bit_slice_to_dec(bit_representation, start, end);
  640. // }
  641. // 15.3. ContractValiditySaleDevice
  642. contract_key = "ContractValiditySaleDevice";
  643. if(is_calypso_node_present(
  644. bit_representation, contract_key, IntercodeContractStructure)) {
  645. int positionOffset = get_calypso_node_offset(
  646. bit_representation, contract_key, IntercodeContractStructure);
  647. int start = positionOffset,
  648. end = positionOffset +
  649. get_calypso_node_size(
  650. contract_key, IntercodeContractStructure) -
  651. 1;
  652. card->navigo->contracts[i - 1].sale_device =
  653. bit_slice_to_dec(bit_representation, start, end);
  654. }
  655. // 16. ContractStatus -- 0x1 ou 0xff
  656. contract_key = "ContractStatus";
  657. if(is_calypso_node_present(
  658. bit_representation, contract_key, IntercodeContractStructure)) {
  659. int positionOffset = get_calypso_node_offset(
  660. bit_representation, contract_key, IntercodeContractStructure);
  661. int start = positionOffset,
  662. end = positionOffset +
  663. get_calypso_node_size(
  664. contract_key, IntercodeContractStructure) -
  665. 1;
  666. card->navigo->contracts[i - 1].status =
  667. bit_slice_to_dec(bit_representation, start, end);
  668. }
  669. // 18. ContractAuthenticator
  670. contract_key = "ContractAuthenticator";
  671. if(is_calypso_node_present(
  672. bit_representation, contract_key, IntercodeContractStructure)) {
  673. int positionOffset = get_calypso_node_offset(
  674. bit_representation, contract_key, IntercodeContractStructure);
  675. int start = positionOffset,
  676. end = positionOffset +
  677. get_calypso_node_size(
  678. contract_key, IntercodeContractStructure) -
  679. 1;
  680. card->navigo->contracts[i - 1].authenticator =
  681. bit_slice_to_dec(bit_representation, start, end);
  682. }
  683. }
  684. // Free the calypso structure
  685. free_calypso_structure(IntercodeContractStructure);
  686. // Select app for counters (remaining tickets on Navigo Easy)
  687. error = select_new_app(
  688. 0x20, 0x69, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  689. if(error != 0) {
  690. break;
  691. }
  692. // Check the response after selecting app
  693. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  694. break;
  695. }
  696. // read file 1
  697. error =
  698. read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  699. if(error != 0) {
  700. break;
  701. }
  702. // Check the response after reading the file
  703. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  704. break;
  705. }
  706. char counter_bit_representation[response_length * 8 + 1];
  707. counter_bit_representation[0] = '\0';
  708. for(size_t i = 0; i < response_length; i++) {
  709. char bits[9];
  710. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  711. byte_to_binary(byte, bits);
  712. strlcat(
  713. counter_bit_representation, bits, sizeof(counter_bit_representation));
  714. }
  715. // FURI_LOG_I(TAG, "Counter bit_representation: %s", counter_bit_representation);
  716. // Ticket counts (contracts 1-4)
  717. for(int i = 0; i < 4; i++) {
  718. start = 0;
  719. end = 5;
  720. card->navigo->contracts[i].counter.count = bit_slice_to_dec(
  721. counter_bit_representation, 24 * i + start, 24 * i + end);
  722. start = 6;
  723. end = 23;
  724. card->navigo->contracts[i].counter.relative_first_stamp_15mn =
  725. bit_slice_to_dec(
  726. counter_bit_representation, 24 * i + start, 24 * i + end);
  727. }
  728. // Select app for events
  729. error = select_new_app(
  730. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  731. if(error != 0) {
  732. break;
  733. }
  734. // Check the response after selecting app
  735. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  736. break;
  737. }
  738. // Load the calypso structure for events
  739. CalypsoApp* IntercodeEventStructure = get_intercode_event_structure();
  740. if(!IntercodeEventStructure) {
  741. FURI_LOG_E(TAG, "Failed to load Intercode Event structure");
  742. break;
  743. }
  744. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  745. // Now send the read command for events
  746. for(size_t i = 1; i < 4; i++) {
  747. error = read_new_file(
  748. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  749. if(error != 0) {
  750. break;
  751. }
  752. // Check the response after reading the file
  753. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  754. break;
  755. }
  756. char event_bit_representation[response_length * 8 + 1];
  757. event_bit_representation[0] = '\0';
  758. for(size_t i = 0; i < response_length; i++) {
  759. char bits[9];
  760. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  761. byte_to_binary(byte, bits);
  762. strlcat(
  763. event_bit_representation, bits, sizeof(event_bit_representation));
  764. }
  765. // furi_string_cat_printf(parsed_data, "Event 0%d :\n", i);
  766. /* int count = 0;
  767. int start = 25, end = 52;
  768. char bit_slice[end - start + 2];
  769. strncpy(bit_slice, event_bit_representation + start, end - start + 1);
  770. bit_slice[end - start + 1] = '\0';
  771. int* positions = get_bit_positions(bit_slice, &count);
  772. FURI_LOG_I(TAG, "Positions: ");
  773. for(int i = 0; i < count; i++) {
  774. FURI_LOG_I(TAG, "%d ", positions[i]);
  775. } */
  776. // 2. EventCode
  777. const char* event_key = "EventCode";
  778. if(is_calypso_node_present(
  779. event_bit_representation, event_key, IntercodeEventStructure)) {
  780. int positionOffset = get_calypso_node_offset(
  781. event_bit_representation, event_key, IntercodeEventStructure);
  782. int start = positionOffset,
  783. end = positionOffset +
  784. get_calypso_node_size(event_key, IntercodeEventStructure) -
  785. 1;
  786. int decimal_value =
  787. bit_slice_to_dec(event_bit_representation, start, end);
  788. card->navigo->events[i - 1].transport_type = decimal_value >> 4;
  789. card->navigo->events[i - 1].transition = decimal_value & 15;
  790. }
  791. // 4. EventServiceProvider
  792. event_key = "EventServiceProvider";
  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. card->navigo->events[i - 1].service_provider =
  801. bit_slice_to_dec(event_bit_representation, start, end);
  802. }
  803. // 8. EventLocationId
  804. event_key = "EventLocationId";
  805. if(is_calypso_node_present(
  806. event_bit_representation, event_key, IntercodeEventStructure)) {
  807. int positionOffset = get_calypso_node_offset(
  808. event_bit_representation, event_key, IntercodeEventStructure);
  809. start = positionOffset,
  810. end = positionOffset +
  811. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  812. int decimal_value =
  813. bit_slice_to_dec(event_bit_representation, start, end);
  814. card->navigo->events[i - 1].station_group_id = decimal_value >> 9;
  815. card->navigo->events[i - 1].station_id = (decimal_value >> 4) & 31;
  816. }
  817. // 9. EventLocationGate
  818. event_key = "EventLocationGate";
  819. if(is_calypso_node_present(
  820. event_bit_representation, event_key, IntercodeEventStructure)) {
  821. int positionOffset = get_calypso_node_offset(
  822. event_bit_representation, event_key, IntercodeEventStructure);
  823. start = positionOffset,
  824. end = positionOffset +
  825. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  826. card->navigo->events[i - 1].location_gate =
  827. bit_slice_to_dec(event_bit_representation, start, end);
  828. card->navigo->events[i - 1].location_gate_available = true;
  829. }
  830. // 10. EventDevice
  831. event_key = "EventDevice";
  832. if(is_calypso_node_present(
  833. event_bit_representation, event_key, IntercodeEventStructure)) {
  834. int positionOffset = get_calypso_node_offset(
  835. event_bit_representation, event_key, IntercodeEventStructure);
  836. start = positionOffset,
  837. end = positionOffset +
  838. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  839. int decimal_value =
  840. bit_slice_to_dec(event_bit_representation, start, end);
  841. card->navigo->events[i - 1].device = decimal_value;
  842. int bus_device = decimal_value >> 8;
  843. card->navigo->events[i - 1].door = bus_device / 2 + 1;
  844. card->navigo->events[i - 1].side = bus_device % 2;
  845. card->navigo->events[i - 1].device_available = true;
  846. }
  847. // 11. EventRouteNumber
  848. event_key = "EventRouteNumber";
  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].route_number =
  857. bit_slice_to_dec(event_bit_representation, start, end);
  858. card->navigo->events[i - 1].route_number_available = true;
  859. }
  860. // 13. EventJourneyRun
  861. event_key = "EventJourneyRun";
  862. if(is_calypso_node_present(
  863. event_bit_representation, event_key, IntercodeEventStructure)) {
  864. int positionOffset = get_calypso_node_offset(
  865. event_bit_representation, event_key, IntercodeEventStructure);
  866. start = positionOffset,
  867. end = positionOffset +
  868. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  869. card->navigo->events[i - 1].mission =
  870. bit_slice_to_dec(event_bit_representation, start, end);
  871. card->navigo->events[i - 1].mission_available = true;
  872. }
  873. // 14. EventVehicleId
  874. event_key = "EventVehicleId";
  875. if(is_calypso_node_present(
  876. event_bit_representation, event_key, IntercodeEventStructure)) {
  877. int positionOffset = get_calypso_node_offset(
  878. event_bit_representation, event_key, IntercodeEventStructure);
  879. start = positionOffset,
  880. end = positionOffset +
  881. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  882. card->navigo->events[i - 1].vehicle_id =
  883. bit_slice_to_dec(event_bit_representation, start, end);
  884. card->navigo->events[i - 1].vehicle_id_available = true;
  885. }
  886. // 25. EventContractPointer
  887. event_key = "EventContractPointer";
  888. if(is_calypso_node_present(
  889. event_bit_representation, event_key, IntercodeEventStructure)) {
  890. int positionOffset = get_calypso_node_offset(
  891. event_bit_representation, event_key, IntercodeEventStructure);
  892. start = positionOffset,
  893. end = positionOffset +
  894. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  895. card->navigo->events[i - 1].used_contract =
  896. bit_slice_to_dec(event_bit_representation, start, end);
  897. card->navigo->events[i - 1].used_contract_available = true;
  898. }
  899. // EventDateStamp
  900. event_key = "EventDateStamp";
  901. int positionOffset = get_calypso_node_offset(
  902. event_bit_representation, event_key, IntercodeEventStructure);
  903. start = positionOffset,
  904. end = positionOffset +
  905. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  906. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  907. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  908. datetime_timestamp_to_datetime(
  909. date_timestamp, &card->navigo->events[i - 1].date);
  910. // EventTimeStamp
  911. event_key = "EventTimeStamp";
  912. positionOffset = get_calypso_node_offset(
  913. event_bit_representation, event_key, IntercodeEventStructure);
  914. start = positionOffset,
  915. end = positionOffset +
  916. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  917. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  918. card->navigo->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  919. card->navigo->events[i - 1].date.minute =
  920. ((decimal_value * 60) % 3600) / 60;
  921. card->navigo->events[i - 1].date.second =
  922. ((decimal_value * 60) % 3600) % 60;
  923. }
  924. // Free the calypso structure
  925. free_calypso_structure(IntercodeEventStructure);
  926. break;
  927. }
  928. case CALYPSO_CARD_OPUS: {
  929. card->opus = malloc(sizeof(OpusCardData));
  930. card->opus->environment.country_num = country_num;
  931. card->opus->environment.network_num = network_num;
  932. CalypsoApp* OpusEnvHolderStructure = get_opus_env_holder_structure();
  933. // EnvApplicationVersionNumber
  934. const char* env_key = "EnvApplicationVersionNumber";
  935. int positionOffset = get_calypso_node_offset(
  936. environment_bit_representation, env_key, OpusEnvHolderStructure);
  937. int start = positionOffset,
  938. end = positionOffset +
  939. get_calypso_node_size(env_key, OpusEnvHolderStructure) - 1;
  940. card->opus->environment.app_version =
  941. bit_slice_to_dec(environment_bit_representation, start, end);
  942. // EnvApplicationValidityEndDate
  943. env_key = "EnvApplicationValidityEndDate";
  944. positionOffset = get_calypso_node_offset(
  945. environment_bit_representation, env_key, OpusEnvHolderStructure);
  946. start = positionOffset,
  947. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  948. 1;
  949. float decimal_value =
  950. bit_slice_to_dec(environment_bit_representation, start, end);
  951. uint64_t end_validity_timestamp =
  952. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  953. datetime_timestamp_to_datetime(
  954. end_validity_timestamp, &card->opus->environment.end_dt);
  955. // HolderDataCardStatus
  956. env_key = "HolderDataCardStatus";
  957. positionOffset = get_calypso_node_offset(
  958. environment_bit_representation, env_key, OpusEnvHolderStructure);
  959. start = positionOffset,
  960. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  961. 1;
  962. card->opus->holder.card_status =
  963. bit_slice_to_dec(environment_bit_representation, start, end);
  964. // HolderDataCommercialID
  965. env_key = "HolderDataCommercialID";
  966. positionOffset = get_calypso_node_offset(
  967. environment_bit_representation, env_key, OpusEnvHolderStructure);
  968. start = positionOffset,
  969. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  970. 1;
  971. card->opus->holder.commercial_id =
  972. bit_slice_to_dec(environment_bit_representation, start, end);
  973. // Free the calypso structure
  974. free_calypso_structure(OpusEnvHolderStructure);
  975. // Select app for contracts
  976. error = select_new_app(
  977. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  978. if(error != 0) {
  979. FURI_LOG_E(TAG, "Failed to select app for contracts");
  980. break;
  981. }
  982. // Check the response after selecting app
  983. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  984. FURI_LOG_E(
  985. TAG, "Failed to check response after selecting app for contracts");
  986. break;
  987. }
  988. // Prepare calypso structure
  989. CalypsoApp* OpusContractStructure = get_opus_contract_structure();
  990. if(!OpusContractStructure) {
  991. FURI_LOG_E(TAG, "Failed to load Opus Contract structure");
  992. break;
  993. }
  994. // Now send the read command for contracts
  995. for(size_t i = 1; i < 5; i++) {
  996. error = read_new_file(
  997. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  998. if(error != 0) {
  999. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1000. break;
  1001. }
  1002. // Check the response after reading the file
  1003. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1004. FURI_LOG_E(
  1005. TAG, "Failed to check response after reading contract %d", i);
  1006. break;
  1007. }
  1008. char bit_representation[response_length * 8 + 1];
  1009. bit_representation[0] = '\0';
  1010. for(size_t i = 0; i < response_length; i++) {
  1011. char bits[9];
  1012. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1013. byte_to_binary(byte, bits);
  1014. strlcat(bit_representation, bits, sizeof(bit_representation));
  1015. }
  1016. bit_representation[response_length * 8] = '\0';
  1017. if(bit_slice_to_dec(
  1018. bit_representation,
  1019. 0,
  1020. OpusContractStructure->container->elements[1].bitmap->size - 1) ==
  1021. 0) {
  1022. break;
  1023. }
  1024. card->opus->contracts[i - 1].present = 1;
  1025. card->contracts_count++;
  1026. // ContractProvider
  1027. const char* contract_key = "ContractProvider";
  1028. if(is_calypso_node_present(
  1029. bit_representation, contract_key, OpusContractStructure)) {
  1030. int positionOffset = get_calypso_node_offset(
  1031. bit_representation, contract_key, OpusContractStructure);
  1032. int start = positionOffset,
  1033. end = positionOffset +
  1034. get_calypso_node_size(contract_key, OpusContractStructure) -
  1035. 1;
  1036. card->opus->contracts[i - 1].provider =
  1037. bit_slice_to_dec(bit_representation, start, end);
  1038. }
  1039. // ContractTariff
  1040. contract_key = "ContractTariff";
  1041. if(is_calypso_node_present(
  1042. bit_representation, contract_key, OpusContractStructure)) {
  1043. int positionOffset = get_calypso_node_offset(
  1044. bit_representation, contract_key, OpusContractStructure);
  1045. int start = positionOffset,
  1046. end = positionOffset +
  1047. get_calypso_node_size(contract_key, OpusContractStructure) -
  1048. 1;
  1049. card->opus->contracts[i - 1].tariff =
  1050. bit_slice_to_dec(bit_representation, start, end);
  1051. }
  1052. // ContractStartDate
  1053. contract_key = "ContractStartDate";
  1054. if(is_calypso_node_present(
  1055. bit_representation, contract_key, OpusContractStructure)) {
  1056. int positionOffset = get_calypso_node_offset(
  1057. bit_representation, contract_key, OpusContractStructure);
  1058. int start = positionOffset,
  1059. end = positionOffset +
  1060. get_calypso_node_size(contract_key, OpusContractStructure) -
  1061. 1;
  1062. float decimal_value =
  1063. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1064. uint64_t start_validity_timestamp =
  1065. (decimal_value + (float)epoch) + 3600;
  1066. datetime_timestamp_to_datetime(
  1067. start_validity_timestamp,
  1068. &card->opus->contracts[i - 1].start_date);
  1069. }
  1070. // ContractEndDate
  1071. contract_key = "ContractEndDate";
  1072. if(is_calypso_node_present(
  1073. bit_representation, contract_key, OpusContractStructure)) {
  1074. int positionOffset = get_calypso_node_offset(
  1075. bit_representation, contract_key, OpusContractStructure);
  1076. int start = positionOffset,
  1077. end = positionOffset +
  1078. get_calypso_node_size(contract_key, OpusContractStructure) -
  1079. 1;
  1080. float decimal_value =
  1081. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1082. uint64_t end_validity_timestamp =
  1083. (decimal_value + (float)epoch) + 3600;
  1084. datetime_timestamp_to_datetime(
  1085. end_validity_timestamp, &card->opus->contracts[i - 1].end_date);
  1086. }
  1087. // ContractStatus
  1088. contract_key = "ContractStatus";
  1089. if(is_calypso_node_present(
  1090. bit_representation, contract_key, OpusContractStructure)) {
  1091. int positionOffset = get_calypso_node_offset(
  1092. bit_representation, contract_key, OpusContractStructure);
  1093. int start = positionOffset,
  1094. end = positionOffset +
  1095. get_calypso_node_size(contract_key, OpusContractStructure) -
  1096. 1;
  1097. card->opus->contracts[i - 1].status =
  1098. bit_slice_to_dec(bit_representation, start, end);
  1099. }
  1100. // ContractSaleDate + ContractSaleTime
  1101. contract_key = "ContractSaleDate";
  1102. int positionOffset = get_calypso_node_offset(
  1103. bit_representation, contract_key, OpusContractStructure);
  1104. int start = positionOffset,
  1105. end = positionOffset +
  1106. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1107. uint64_t sale_date_timestamp =
  1108. (bit_slice_to_dec(bit_representation, start, end) + (float)epoch) +
  1109. 3600;
  1110. datetime_timestamp_to_datetime(
  1111. sale_date_timestamp, &card->opus->contracts[i - 1].sale_date);
  1112. contract_key = "ContractSaleTime";
  1113. positionOffset = get_calypso_node_offset(
  1114. bit_representation, contract_key, OpusContractStructure);
  1115. start = positionOffset,
  1116. end = positionOffset +
  1117. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1118. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  1119. card->opus->contracts[i - 1].sale_date.hour = (decimal_value * 60) / 3600;
  1120. card->opus->contracts[i - 1].sale_date.minute =
  1121. ((decimal_value * 60) % 3600) / 60;
  1122. card->opus->contracts[i - 1].sale_date.second =
  1123. ((decimal_value * 60) % 3600) % 60;
  1124. }
  1125. // Free the calypso structure
  1126. free_calypso_structure(OpusContractStructure);
  1127. // Select app for events
  1128. error = select_new_app(
  1129. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1130. if(error != 0) {
  1131. break;
  1132. }
  1133. // Check the response after selecting app
  1134. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1135. break;
  1136. }
  1137. // Load the calypso structure for events
  1138. CalypsoApp* OpusEventStructure = get_opus_event_structure();
  1139. if(!OpusEventStructure) {
  1140. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1141. break;
  1142. }
  1143. // Now send the read command for events
  1144. for(size_t i = 1; i < 4; i++) {
  1145. error = read_new_file(
  1146. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1147. if(error != 0) {
  1148. break;
  1149. }
  1150. // Check the response after reading the file
  1151. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1152. break;
  1153. }
  1154. char event_bit_representation[response_length * 8 + 1];
  1155. event_bit_representation[0] = '\0';
  1156. for(size_t i = 0; i < response_length; i++) {
  1157. char bits[9];
  1158. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1159. byte_to_binary(byte, bits);
  1160. strlcat(
  1161. event_bit_representation, bits, sizeof(event_bit_representation));
  1162. }
  1163. // EventServiceProvider
  1164. const char* event_key = "EventServiceProvider";
  1165. if(is_calypso_node_present(
  1166. event_bit_representation, event_key, OpusEventStructure)) {
  1167. int positionOffset = get_calypso_node_offset(
  1168. event_bit_representation, event_key, OpusEventStructure);
  1169. int start = positionOffset,
  1170. end = positionOffset +
  1171. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1172. card->opus->events[i - 1].service_provider =
  1173. bit_slice_to_dec(event_bit_representation, start, end);
  1174. }
  1175. // EventRouteNumber
  1176. event_key = "EventRouteNumber";
  1177. if(is_calypso_node_present(
  1178. event_bit_representation, event_key, OpusEventStructure)) {
  1179. int positionOffset = get_calypso_node_offset(
  1180. event_bit_representation, event_key, OpusEventStructure);
  1181. int start = positionOffset,
  1182. end = positionOffset +
  1183. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1184. card->opus->events[i - 1].route_number =
  1185. bit_slice_to_dec(event_bit_representation, start, end);
  1186. card->opus->events[i - 1].route_number_available = true;
  1187. }
  1188. // EventContractPointer
  1189. event_key = "EventContractPointer";
  1190. if(is_calypso_node_present(
  1191. event_bit_representation, event_key, OpusEventStructure)) {
  1192. int positionOffset = get_calypso_node_offset(
  1193. event_bit_representation, event_key, OpusEventStructure);
  1194. int start = positionOffset,
  1195. end = positionOffset +
  1196. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1197. card->opus->events[i - 1].used_contract =
  1198. bit_slice_to_dec(event_bit_representation, start, end);
  1199. card->opus->events[i - 1].used_contract_available = true;
  1200. }
  1201. // EventDate + EventTime
  1202. event_key = "EventDate";
  1203. int positionOffset = get_calypso_node_offset(
  1204. event_bit_representation, event_key, OpusEventStructure);
  1205. int start = positionOffset,
  1206. end = positionOffset +
  1207. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1208. uint64_t date_timestamp =
  1209. (bit_slice_to_dec(event_bit_representation, start, end) +
  1210. (float)epoch) +
  1211. 3600;
  1212. datetime_timestamp_to_datetime(
  1213. date_timestamp, &card->opus->events[i - 1].date);
  1214. event_key = "EventTime";
  1215. positionOffset = get_calypso_node_offset(
  1216. event_bit_representation, event_key, OpusEventStructure);
  1217. start = positionOffset,
  1218. end = positionOffset +
  1219. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1220. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1221. card->opus->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1222. card->opus->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  1223. card->opus->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  1224. }
  1225. // Free the calypso structure
  1226. free_calypso_structure(OpusEventStructure);
  1227. break;
  1228. }
  1229. case CALYPSO_CARD_UNKNOWN: {
  1230. start = 3;
  1231. end = 6;
  1232. country_num =
  1233. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1234. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1235. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1236. start = 15;
  1237. end = 18;
  1238. network_num =
  1239. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1240. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1241. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1242. card->card_type = guess_card_type(country_num, network_num);
  1243. if(card->card_type == CALYPSO_CARD_RAVKAV) {
  1244. card->ravkav = malloc(sizeof(RavKavCardData));
  1245. error = select_new_app(
  1246. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1247. if(error != 0) {
  1248. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1249. break;
  1250. }
  1251. // Check the response after selecting app
  1252. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1253. FURI_LOG_E(
  1254. TAG, "Failed to check response after selecting app for contracts");
  1255. break;
  1256. }
  1257. // Prepare calypso structure
  1258. CalypsoApp* RavKavContractStructure = get_ravkav_contract_structure();
  1259. if(!RavKavContractStructure) {
  1260. FURI_LOG_E(TAG, "Failed to load RavKav Contract structure");
  1261. break;
  1262. }
  1263. // Now send the read command for contracts
  1264. for(size_t i = 1; i < 2; i++) {
  1265. error = read_new_file(
  1266. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1267. if(error != 0) {
  1268. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1269. break;
  1270. }
  1271. // Check the response after reading the file
  1272. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1273. FURI_LOG_E(
  1274. TAG, "Failed to check response after reading contract %d", i);
  1275. break;
  1276. }
  1277. char bit_representation[response_length * 8 + 1];
  1278. bit_representation[0] = '\0';
  1279. for(size_t i = 0; i < response_length; i++) {
  1280. char bits[9];
  1281. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1282. byte_to_binary(byte, bits);
  1283. strlcat(bit_representation, bits, sizeof(bit_representation));
  1284. }
  1285. bit_representation[response_length * 8] = '\0';
  1286. card->ravkav->contracts[i - 1].present = 1;
  1287. card->contracts_count++;
  1288. // ContractVersion
  1289. const char* contract_key = "ContractVersion";
  1290. if(is_calypso_node_present(
  1291. bit_representation, contract_key, RavKavContractStructure)) {
  1292. int positionOffset = get_calypso_node_offset(
  1293. bit_representation, contract_key, RavKavContractStructure);
  1294. int start = positionOffset,
  1295. end = positionOffset +
  1296. get_calypso_node_size(
  1297. contract_key, RavKavContractStructure) -
  1298. 1;
  1299. card->ravkav->contracts[i - 1].version =
  1300. bit_slice_to_dec(bit_representation, start, end);
  1301. }
  1302. // ContractStartDate
  1303. contract_key = "ContractStartDate";
  1304. if(is_calypso_node_present(
  1305. bit_representation, contract_key, RavKavContractStructure)) {
  1306. int positionOffset = get_calypso_node_offset(
  1307. bit_representation, contract_key, RavKavContractStructure);
  1308. int start = positionOffset,
  1309. end = positionOffset +
  1310. get_calypso_node_size(
  1311. contract_key, RavKavContractStructure) -
  1312. 1;
  1313. int decimal_value =
  1314. bit_slice_to_dec(bit_representation, start, end);
  1315. uint32_t invertedDays = decimal_value ^ 0x3FFF;
  1316. int start_validity_timestamp =
  1317. (invertedDays * 3600 * 24) + epoch + 3600;
  1318. datetime_timestamp_to_datetime(
  1319. start_validity_timestamp,
  1320. &card->ravkav->contracts[i - 1].start_date);
  1321. }
  1322. // ContractProvider
  1323. contract_key = "ContractProvider";
  1324. if(is_calypso_node_present(
  1325. bit_representation, contract_key, RavKavContractStructure)) {
  1326. int positionOffset = get_calypso_node_offset(
  1327. bit_representation, contract_key, RavKavContractStructure);
  1328. int start = positionOffset,
  1329. end = positionOffset +
  1330. get_calypso_node_size(
  1331. contract_key, RavKavContractStructure) -
  1332. 1;
  1333. card->ravkav->contracts[i - 1].provider =
  1334. bit_slice_to_dec(bit_representation, start, end);
  1335. FURI_LOG_I(
  1336. TAG,
  1337. "issuer number: %d",
  1338. card->ravkav->contracts[i - 1].provider);
  1339. }
  1340. // ContractTariff
  1341. contract_key = "ContractTariff";
  1342. if(is_calypso_node_present(
  1343. bit_representation, contract_key, RavKavContractStructure)) {
  1344. int positionOffset = get_calypso_node_offset(
  1345. bit_representation, contract_key, RavKavContractStructure);
  1346. int start = positionOffset,
  1347. end = positionOffset +
  1348. get_calypso_node_size(
  1349. contract_key, RavKavContractStructure) -
  1350. 1;
  1351. card->ravkav->contracts[i - 1].tariff =
  1352. bit_slice_to_dec(bit_representation, start, end);
  1353. }
  1354. // ContractSaleDate
  1355. contract_key = "ContractSaleDate";
  1356. int positionOffset = get_calypso_node_offset(
  1357. bit_representation, contract_key, RavKavContractStructure);
  1358. int start = positionOffset,
  1359. end =
  1360. positionOffset +
  1361. get_calypso_node_size(contract_key, RavKavContractStructure) -
  1362. 1;
  1363. uint64_t sale_date_timestamp =
  1364. (bit_slice_to_dec(bit_representation, start, end) * 3600 * 24) +
  1365. (float)epoch + 3600;
  1366. datetime_timestamp_to_datetime(
  1367. sale_date_timestamp, &card->ravkav->contracts[i - 1].sale_date);
  1368. // ContractSaleDevice
  1369. contract_key = "ContractSaleDevice";
  1370. if(is_calypso_node_present(
  1371. bit_representation, contract_key, RavKavContractStructure)) {
  1372. int positionOffset = get_calypso_node_offset(
  1373. bit_representation, contract_key, RavKavContractStructure);
  1374. int start = positionOffset,
  1375. end = positionOffset +
  1376. get_calypso_node_size(
  1377. contract_key, RavKavContractStructure) -
  1378. 1;
  1379. card->ravkav->contracts[i - 1].sale_device =
  1380. bit_slice_to_dec(bit_representation, start, end);
  1381. }
  1382. // ContractSaleNumber
  1383. contract_key = "ContractSaleNumber";
  1384. if(is_calypso_node_present(
  1385. bit_representation, contract_key, RavKavContractStructure)) {
  1386. int positionOffset = get_calypso_node_offset(
  1387. bit_representation, contract_key, RavKavContractStructure);
  1388. int start = positionOffset,
  1389. end = positionOffset +
  1390. get_calypso_node_size(
  1391. contract_key, RavKavContractStructure) -
  1392. 1;
  1393. card->ravkav->contracts[i - 1].sale_number =
  1394. bit_slice_to_dec(bit_representation, start, end);
  1395. }
  1396. // ContractInterchange
  1397. contract_key = "ContractInterchange";
  1398. if(is_calypso_node_present(
  1399. bit_representation, contract_key, RavKavContractStructure)) {
  1400. int positionOffset = get_calypso_node_offset(
  1401. bit_representation, contract_key, RavKavContractStructure);
  1402. int start = positionOffset,
  1403. end = positionOffset +
  1404. get_calypso_node_size(
  1405. contract_key, RavKavContractStructure) -
  1406. 1;
  1407. card->ravkav->contracts[i - 1].interchange =
  1408. bit_slice_to_dec(bit_representation, start, end);
  1409. }
  1410. // ContractInterchange
  1411. contract_key = "ContractRestrictCode";
  1412. if(is_calypso_node_present(
  1413. bit_representation, contract_key, RavKavContractStructure)) {
  1414. int positionOffset = get_calypso_node_offset(
  1415. bit_representation, contract_key, RavKavContractStructure);
  1416. int start = positionOffset,
  1417. end = positionOffset +
  1418. get_calypso_node_size(
  1419. contract_key, RavKavContractStructure) -
  1420. 1;
  1421. card->ravkav->contracts[i - 1].restrict_code_available = true;
  1422. card->ravkav->contracts[i - 1].restrict_code =
  1423. bit_slice_to_dec(bit_representation, start, end);
  1424. }
  1425. // ContractRestrictDuration
  1426. contract_key = "ContractRestrictDuration";
  1427. if(is_calypso_node_present(
  1428. bit_representation, contract_key, RavKavContractStructure)) {
  1429. int positionOffset = get_calypso_node_offset(
  1430. bit_representation, contract_key, RavKavContractStructure);
  1431. int start = positionOffset,
  1432. end = positionOffset +
  1433. get_calypso_node_size(
  1434. contract_key, RavKavContractStructure) -
  1435. 1;
  1436. card->ravkav->contracts[i - 1].restrict_duration_available = true;
  1437. if(card->ravkav->contracts[i - 1].restrict_code == 16) {
  1438. card->ravkav->contracts[i - 1].restrict_duration =
  1439. bit_slice_to_dec(bit_representation, start, end) * 5;
  1440. } else {
  1441. card->ravkav->contracts[i - 1].restrict_duration =
  1442. bit_slice_to_dec(bit_representation, start, end) * 30;
  1443. }
  1444. }
  1445. // ContractEndDate
  1446. contract_key = "ContractEndDate";
  1447. if(is_calypso_node_present(
  1448. bit_representation, contract_key, RavKavContractStructure)) {
  1449. int positionOffset = get_calypso_node_offset(
  1450. bit_representation, contract_key, RavKavContractStructure);
  1451. int start = positionOffset,
  1452. end = positionOffset +
  1453. get_calypso_node_size(
  1454. contract_key, RavKavContractStructure) -
  1455. 1;
  1456. card->ravkav->contracts[i - 1].end_date_available = true;
  1457. int end_date_timestamp =
  1458. (bit_slice_to_dec(bit_representation, start, end) * 3600 *
  1459. 24) +
  1460. epoch + 3600;
  1461. datetime_timestamp_to_datetime(
  1462. end_date_timestamp, &card->ravkav->contracts[i - 1].end_date);
  1463. }
  1464. }
  1465. // Free the calypso structure
  1466. free_calypso_structure(RavKavContractStructure);
  1467. error = select_new_app(
  1468. 0x20, 0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1469. if(error != 0) {
  1470. FURI_LOG_E(TAG, "Failed to select app for environment");
  1471. break;
  1472. }
  1473. // Check the response after selecting app
  1474. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1475. FURI_LOG_E(
  1476. TAG,
  1477. "Failed to check response after selecting app for environment");
  1478. break;
  1479. }
  1480. // Prepare calypso structure
  1481. CalypsoApp* RavKavEnvStructure = get_ravkav_env_holder_structure();
  1482. if(!RavKavEnvStructure) {
  1483. FURI_LOG_E(TAG, "Failed to load RavKav environment structure");
  1484. break;
  1485. }
  1486. // Now send the read command for environment
  1487. error = read_new_file(
  1488. 1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1489. if(error != 0) {
  1490. FURI_LOG_E(TAG, "Failed to read environment");
  1491. break;
  1492. }
  1493. // Check the response after reading the file
  1494. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1495. FURI_LOG_E(TAG, "Failed to check response after reading environment");
  1496. break;
  1497. }
  1498. char env_bit_representation[response_length * 8 + 1];
  1499. env_bit_representation[0] = '\0';
  1500. for(size_t i = 0; i < response_length; i++) {
  1501. char bits[9];
  1502. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1503. byte_to_binary(byte, bits);
  1504. strlcat(env_bit_representation, bits, sizeof(env_bit_representation));
  1505. }
  1506. env_bit_representation[response_length * 8] = '\0';
  1507. // EnvApplicationVersionNumber
  1508. char* env_key = "EnvApplicationVersionNumber";
  1509. if(is_calypso_node_present(
  1510. env_bit_representation, env_key, RavKavEnvStructure)) {
  1511. int positionOffset = get_calypso_node_offset(
  1512. env_bit_representation, env_key, RavKavEnvStructure);
  1513. int start = positionOffset,
  1514. end = positionOffset +
  1515. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1516. card->ravkav->environment.app_num =
  1517. bit_slice_to_dec(env_bit_representation, start, end);
  1518. }
  1519. // EnvApplicationNumber
  1520. env_key = "EnvApplicationNumber";
  1521. if(is_calypso_node_present(
  1522. env_bit_representation, env_key, RavKavEnvStructure)) {
  1523. int positionOffset = get_calypso_node_offset(
  1524. env_bit_representation, env_key, RavKavEnvStructure);
  1525. int start = positionOffset,
  1526. end = positionOffset +
  1527. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1528. card->ravkav->environment.app_num =
  1529. bit_slice_to_dec(env_bit_representation, start, end);
  1530. }
  1531. // EnvDateOfIssue
  1532. env_key = "EnvDateOfIssue";
  1533. if(is_calypso_node_present(
  1534. env_bit_representation, env_key, RavKavEnvStructure)) {
  1535. int positionOffset = get_calypso_node_offset(
  1536. env_bit_representation, env_key, RavKavEnvStructure);
  1537. int start = positionOffset,
  1538. end = positionOffset +
  1539. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1540. uint64_t issue_date_timestamp =
  1541. (bit_slice_to_dec(env_bit_representation, start, end) * 3600 *
  1542. 24) +
  1543. (float)epoch + 3600;
  1544. datetime_timestamp_to_datetime(
  1545. issue_date_timestamp, &card->ravkav->environment.issue_dt);
  1546. }
  1547. // EnvEndValidity
  1548. env_key = "EnvEndValidity";
  1549. if(is_calypso_node_present(
  1550. env_bit_representation, env_key, RavKavEnvStructure)) {
  1551. int positionOffset = get_calypso_node_offset(
  1552. env_bit_representation, env_key, RavKavEnvStructure);
  1553. int start = positionOffset,
  1554. end = positionOffset +
  1555. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1556. uint64_t end_date_timestamp =
  1557. (bit_slice_to_dec(env_bit_representation, start, end) * 3600 *
  1558. 24) +
  1559. (float)epoch + 3600;
  1560. datetime_timestamp_to_datetime(
  1561. end_date_timestamp, &card->ravkav->environment.end_dt);
  1562. }
  1563. // EnvPayMethod
  1564. env_key = "EnvPayMethod";
  1565. if(is_calypso_node_present(
  1566. env_bit_representation, env_key, RavKavEnvStructure)) {
  1567. int positionOffset = get_calypso_node_offset(
  1568. env_bit_representation, env_key, RavKavEnvStructure);
  1569. int start = positionOffset,
  1570. end = positionOffset +
  1571. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1572. card->ravkav->environment.pay_method =
  1573. bit_slice_to_dec(env_bit_representation, start, end);
  1574. }
  1575. free_calypso_structure(RavKavEnvStructure);
  1576. // Select app for events
  1577. error = select_new_app(
  1578. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1579. if(error != 0) {
  1580. break;
  1581. }
  1582. // Check the response after selecting app
  1583. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1584. break;
  1585. }
  1586. // Load the calypso structure for events
  1587. CalypsoApp* RavKavEventStructure = get_ravkav_event_structure();
  1588. if(!RavKavEventStructure) {
  1589. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1590. break;
  1591. }
  1592. // Now send the read command for events
  1593. for(size_t i = 1; i < 4; i++) {
  1594. error = read_new_file(
  1595. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1596. if(error != 0) {
  1597. break;
  1598. }
  1599. // Check the response after reading the file
  1600. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1601. break;
  1602. }
  1603. char event_bit_representation[response_length * 8 + 1];
  1604. event_bit_representation[0] = '\0';
  1605. for(size_t i = 0; i < response_length; i++) {
  1606. char bits[9];
  1607. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1608. byte_to_binary(byte, bits);
  1609. strlcat(
  1610. event_bit_representation,
  1611. bits,
  1612. sizeof(event_bit_representation));
  1613. }
  1614. FURI_LOG_I(TAG, "event bit repr %s", event_bit_representation);
  1615. // EventVersion
  1616. const char* event_key = "EventVersion";
  1617. if(is_calypso_node_present(
  1618. event_bit_representation, event_key, RavKavEventStructure)) {
  1619. int positionOffset = get_calypso_node_offset(
  1620. event_bit_representation, event_key, RavKavEventStructure);
  1621. int start = positionOffset,
  1622. end = positionOffset +
  1623. get_calypso_node_size(event_key, RavKavEventStructure) -
  1624. 1;
  1625. card->ravkav->events[i - 1].event_version =
  1626. bit_slice_to_dec(event_bit_representation, start, end);
  1627. }
  1628. // EventServiceProvider
  1629. event_key = "EventServiceProvider";
  1630. if(is_calypso_node_present(
  1631. event_bit_representation, event_key, RavKavEventStructure)) {
  1632. int positionOffset = get_calypso_node_offset(
  1633. event_bit_representation, event_key, RavKavEventStructure);
  1634. int start = positionOffset,
  1635. end = positionOffset +
  1636. get_calypso_node_size(event_key, RavKavEventStructure) -
  1637. 1;
  1638. FURI_LOG_I(TAG, "service provider: start: %d, end %d", start, end);
  1639. card->ravkav->events[i - 1].service_provider =
  1640. bit_slice_to_dec(event_bit_representation, start, end);
  1641. }
  1642. // EventContractID
  1643. event_key = "EventContractID";
  1644. if(is_calypso_node_present(
  1645. event_bit_representation, event_key, RavKavEventStructure)) {
  1646. int positionOffset = get_calypso_node_offset(
  1647. event_bit_representation, event_key, RavKavEventStructure);
  1648. int start = positionOffset,
  1649. end = positionOffset +
  1650. get_calypso_node_size(event_key, RavKavEventStructure) -
  1651. 1;
  1652. card->ravkav->events[i - 1].contract_id =
  1653. bit_slice_to_dec(event_bit_representation, start, end);
  1654. FURI_LOG_I(TAG, "2: start: %d, end %d", start, end);
  1655. }
  1656. // EventAreaID
  1657. event_key = "EventAreaID";
  1658. if(is_calypso_node_present(
  1659. event_bit_representation, event_key, RavKavEventStructure)) {
  1660. int positionOffset = get_calypso_node_offset(
  1661. event_bit_representation, event_key, RavKavEventStructure);
  1662. int start = positionOffset,
  1663. end = positionOffset +
  1664. get_calypso_node_size(event_key, RavKavEventStructure) -
  1665. 1;
  1666. card->ravkav->events[i - 1].area_id =
  1667. bit_slice_to_dec(event_bit_representation, start, end);
  1668. FURI_LOG_I(TAG, "3: start: %d, end %d", start, end);
  1669. }
  1670. // EventType
  1671. event_key = "EventType";
  1672. if(is_calypso_node_present(
  1673. event_bit_representation, event_key, RavKavEventStructure)) {
  1674. int positionOffset = get_calypso_node_offset(
  1675. event_bit_representation, event_key, RavKavEventStructure);
  1676. int start = positionOffset,
  1677. end = positionOffset +
  1678. get_calypso_node_size(event_key, RavKavEventStructure) -
  1679. 1;
  1680. card->ravkav->events[i - 1].type =
  1681. bit_slice_to_dec(event_bit_representation, start, end);
  1682. FURI_LOG_I(TAG, "4: start: %d, end %d", start, end);
  1683. }
  1684. // EventRouteNumber
  1685. event_key = "EventExtension";
  1686. if(is_calypso_node_present(
  1687. event_bit_representation, event_key, RavKavEventStructure)) {
  1688. int positionOffset = get_calypso_node_offset(
  1689. event_bit_representation, event_key, RavKavEventStructure);
  1690. int start = positionOffset,
  1691. end = positionOffset +
  1692. get_calypso_node_size(event_key, RavKavEventStructure) -
  1693. 1;
  1694. FURI_LOG_I(TAG, "event extension : start: %d, end %d", start, end);
  1695. FURI_LOG_I(
  1696. TAG,
  1697. "event extension bitmap: %d",
  1698. bit_slice_to_dec(event_bit_representation, start, end));
  1699. }
  1700. // EventTime
  1701. event_key = "EventTime";
  1702. if(is_calypso_node_present(
  1703. event_bit_representation, event_key, RavKavEventStructure)) {
  1704. int positionOffset = get_calypso_node_offset(
  1705. event_bit_representation, event_key, RavKavEventStructure);
  1706. int start = positionOffset,
  1707. end = positionOffset +
  1708. get_calypso_node_size(event_key, RavKavEventStructure) -
  1709. 1;
  1710. uint64_t event_timestamp =
  1711. bit_slice_to_dec(event_bit_representation, start, end) +
  1712. (float)epoch + 3600;
  1713. datetime_timestamp_to_datetime(
  1714. event_timestamp, &card->ravkav->events[i - 1].time);
  1715. FURI_LOG_I(TAG, "5: start: %d, end %d", start, end);
  1716. }
  1717. // EventInterchangeFlag
  1718. event_key = "EventInterchangeFlag";
  1719. if(is_calypso_node_present(
  1720. event_bit_representation, event_key, RavKavEventStructure)) {
  1721. int positionOffset = get_calypso_node_offset(
  1722. event_bit_representation, event_key, RavKavEventStructure);
  1723. int start = positionOffset,
  1724. end = positionOffset +
  1725. get_calypso_node_size(event_key, RavKavEventStructure) -
  1726. 1;
  1727. card->ravkav->events[i - 1].interchange_flag =
  1728. bit_slice_to_dec(event_bit_representation, start, end);
  1729. FURI_LOG_I(TAG, "6: start: %d, end %d", start, end);
  1730. }
  1731. // EventRouteNumber
  1732. event_key = "EventRouteNumber";
  1733. if(is_calypso_node_present(
  1734. event_bit_representation, event_key, RavKavEventStructure)) {
  1735. int positionOffset = get_calypso_node_offset(
  1736. event_bit_representation, event_key, RavKavEventStructure);
  1737. int start = positionOffset,
  1738. end = positionOffset +
  1739. get_calypso_node_size(event_key, RavKavEventStructure) -
  1740. 1;
  1741. card->ravkav->events[i - 1].route_number =
  1742. bit_slice_to_dec(event_bit_representation, start, end);
  1743. card->ravkav->events[i - 1].route_number_available = true;
  1744. FURI_LOG_I(TAG, "7: start: %d, end %d", start, end);
  1745. }
  1746. // EventRouteNumber
  1747. event_key = "EventfareCode";
  1748. if(is_calypso_node_present(
  1749. event_bit_representation, event_key, RavKavEventStructure)) {
  1750. int positionOffset = get_calypso_node_offset(
  1751. event_bit_representation, event_key, RavKavEventStructure);
  1752. int start = positionOffset,
  1753. end = positionOffset +
  1754. get_calypso_node_size(event_key, RavKavEventStructure) -
  1755. 1;
  1756. card->ravkav->events[i - 1].fare_code =
  1757. bit_slice_to_dec(event_bit_representation, start, end);
  1758. card->ravkav->events[i - 1].fare_code = true;
  1759. FURI_LOG_I(TAG, "8: start: %d, end %d", start, end);
  1760. }
  1761. // EventRouteNumber
  1762. event_key = "EventDebitAmount";
  1763. if(is_calypso_node_present(
  1764. event_bit_representation, event_key, RavKavEventStructure)) {
  1765. int positionOffset = get_calypso_node_offset(
  1766. event_bit_representation, event_key, RavKavEventStructure);
  1767. int start = positionOffset,
  1768. end = positionOffset +
  1769. get_calypso_node_size(event_key, RavKavEventStructure) -
  1770. 1;
  1771. card->ravkav->events[i - 1].debit_amount =
  1772. bit_slice_to_dec(event_bit_representation, start, end) / 100.0;
  1773. card->ravkav->events[i - 1].debit_amount_available = true;
  1774. FURI_LOG_I(TAG, "9: start: %d, end %d", start, end);
  1775. }
  1776. // EventRouteNumber
  1777. event_key = "Location";
  1778. if(is_calypso_node_present(
  1779. event_bit_representation, event_key, RavKavEventStructure)) {
  1780. int positionOffset = get_calypso_node_offset(
  1781. event_bit_representation, event_key, RavKavEventStructure);
  1782. int start = positionOffset,
  1783. end = positionOffset +
  1784. get_calypso_node_size(event_key, RavKavEventStructure) -
  1785. 1;
  1786. FURI_LOG_I(TAG, "location : start: %d, end %d", start, end);
  1787. FURI_LOG_I(
  1788. TAG,
  1789. "locatrion bitmap: %d",
  1790. bit_slice_to_dec(event_bit_representation, start, end));
  1791. }
  1792. }
  1793. // Free the calypso structure
  1794. free_calypso_structure(RavKavEventStructure);
  1795. break;
  1796. }
  1797. }
  1798. default:
  1799. break;
  1800. }
  1801. widget_add_text_scroll_element(
  1802. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1803. CalypsoContext* context = malloc(sizeof(CalypsoContext));
  1804. context->card = card;
  1805. context->page_id = 0;
  1806. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  1807. app->calypso_context = context;
  1808. // Ensure no nested mutexes
  1809. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1810. update_page_info(app, parsed_data);
  1811. furi_mutex_release(context->mutex);
  1812. widget_add_text_scroll_element(
  1813. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1814. // Ensure no nested mutexes
  1815. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1816. update_widget_elements(app);
  1817. furi_mutex_release(context->mutex);
  1818. furi_string_free(parsed_data);
  1819. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  1820. metroflip_app_blink_stop(app);
  1821. stage = MetroflipPollerEventTypeSuccess;
  1822. next_command = NfcCommandStop;
  1823. } while(false);
  1824. if(stage != MetroflipPollerEventTypeSuccess) {
  1825. next_command = NfcCommandStop;
  1826. }
  1827. }
  1828. }
  1829. bit_buffer_free(tx_buffer);
  1830. bit_buffer_free(rx_buffer);
  1831. return next_command;
  1832. }
  1833. void metroflip_scene_calypso_on_enter(void* context) {
  1834. Metroflip* app = context;
  1835. dolphin_deed(DolphinDeedNfcRead);
  1836. // Setup view
  1837. Popup* popup = app->popup;
  1838. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  1839. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  1840. // Start worker
  1841. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  1842. nfc_scanner_alloc(app->nfc);
  1843. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  1844. nfc_poller_start(app->poller, metroflip_scene_calypso_poller_callback, app);
  1845. metroflip_app_blink_start(app);
  1846. }
  1847. bool metroflip_scene_calypso_on_event(void* context, SceneManagerEvent event) {
  1848. Metroflip* app = context;
  1849. bool consumed = false;
  1850. if(event.type == SceneManagerEventTypeCustom) {
  1851. if(event.event == MetroflipPollerEventTypeCardDetect) {
  1852. Popup* popup = app->popup;
  1853. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  1854. consumed = true;
  1855. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  1856. Popup* popup = app->popup;
  1857. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  1858. consumed = true;
  1859. } else if(event.event == MetroflipCustomEventPollerFail) {
  1860. Popup* popup = app->popup;
  1861. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  1862. consumed = true;
  1863. }
  1864. } else if(event.type == SceneManagerEventTypeBack) {
  1865. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  1866. consumed = true;
  1867. }
  1868. return consumed;
  1869. }
  1870. void metroflip_scene_calypso_on_exit(void* context) {
  1871. Metroflip* app = context;
  1872. if(app->poller) {
  1873. nfc_poller_stop(app->poller);
  1874. nfc_poller_free(app->poller);
  1875. }
  1876. metroflip_app_blink_stop(app);
  1877. widget_reset(app->widget);
  1878. // Clear view
  1879. popup_reset(app->popup);
  1880. if(app->calypso_context) {
  1881. CalypsoContext* ctx = app->calypso_context;
  1882. free(ctx->card->navigo);
  1883. free(ctx->card->opus);
  1884. free(ctx->card);
  1885. furi_mutex_free(ctx->mutex);
  1886. free(ctx);
  1887. app->calypso_context = NULL;
  1888. }
  1889. }