calypso.c 134 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572
  1. #include "../../api/metroflip/metroflip_api.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 "../../api/metroflip/metroflip_api.h"
  8. #include "../../metroflip_plugins.h"
  9. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  10. #define TAG "Metroflip:Scene:Calypso"
  11. int select_new_app(
  12. int new_app_directory,
  13. int new_app,
  14. BitBuffer* tx_buffer,
  15. BitBuffer* rx_buffer,
  16. Iso14443_4bPoller* iso14443_4b_poller,
  17. Metroflip* app,
  18. MetroflipPollerEventType* stage) {
  19. select_app[5] = new_app_directory;
  20. select_app[6] = new_app;
  21. bit_buffer_reset(tx_buffer);
  22. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  23. FURI_LOG_D(
  24. TAG,
  25. "SEND %02x %02x %02x %02x %02x %02x %02x %02x",
  26. select_app[0],
  27. select_app[1],
  28. select_app[2],
  29. select_app[3],
  30. select_app[4],
  31. select_app[5],
  32. select_app[6],
  33. select_app[7]);
  34. int error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  35. if(error != Iso14443_4bErrorNone) {
  36. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  37. *stage = MetroflipPollerEventTypeFail;
  38. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  39. return error;
  40. }
  41. return 0;
  42. }
  43. int read_new_file(
  44. int new_file,
  45. BitBuffer* tx_buffer,
  46. BitBuffer* rx_buffer,
  47. Iso14443_4bPoller* iso14443_4b_poller,
  48. Metroflip* app,
  49. MetroflipPollerEventType* stage) {
  50. read_file[2] = new_file;
  51. bit_buffer_reset(tx_buffer);
  52. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  53. FURI_LOG_D(
  54. TAG,
  55. "SEND %02x %02x %02x %02x %02x",
  56. read_file[0],
  57. read_file[1],
  58. read_file[2],
  59. read_file[3],
  60. read_file[4]);
  61. Iso14443_4bError error =
  62. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  63. if(error != Iso14443_4bErrorNone) {
  64. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  65. *stage = MetroflipPollerEventTypeFail;
  66. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  67. return error;
  68. }
  69. return 0;
  70. }
  71. int check_response(
  72. BitBuffer* rx_buffer,
  73. Metroflip* app,
  74. MetroflipPollerEventType* stage,
  75. size_t* response_length) {
  76. *response_length = bit_buffer_get_size_bytes(rx_buffer);
  77. if(bit_buffer_get_byte(rx_buffer, *response_length - 2) != apdu_success[0] ||
  78. bit_buffer_get_byte(rx_buffer, *response_length - 1) != apdu_success[1]) {
  79. int error_code_1 = bit_buffer_get_byte(rx_buffer, *response_length - 2);
  80. int error_code_2 = bit_buffer_get_byte(rx_buffer, *response_length - 1);
  81. FURI_LOG_E(TAG, "Select profile app/file failed: %02x%02x", error_code_1, error_code_2);
  82. if(error_code_1 == 0x6a && error_code_2 == 0x82) {
  83. FURI_LOG_E(TAG, "Wrong parameter(s) P1-P2 - File not found");
  84. } else if(error_code_1 == 0x69 && error_code_2 == 0x82) {
  85. FURI_LOG_E(TAG, "Command not allowed - Security status not satisfied");
  86. }
  87. *stage = MetroflipPollerEventTypeFail;
  88. view_dispatcher_send_custom_event(
  89. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  90. return 1;
  91. }
  92. return 0;
  93. }
  94. void update_page_info(void* context, FuriString* parsed_data) {
  95. Metroflip* app = context;
  96. CalypsoContext* ctx = app->calypso_context;
  97. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO && ctx->card->card_type != CALYPSO_CARD_OPUS &&
  98. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  99. furi_string_cat_printf(
  100. parsed_data,
  101. "\e#%s %u:\n",
  102. get_network_string(ctx->card->card_type),
  103. ctx->card->card_number);
  104. return;
  105. }
  106. if(ctx->page_id == 0) {
  107. switch(ctx->card->card_type) {
  108. case CALYPSO_CARD_NAVIGO: {
  109. furi_string_cat_printf(parsed_data, "\e#Navigo %u:\n", ctx->card->card_number);
  110. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  111. show_navigo_environment_info(
  112. &ctx->card->navigo->environment, &ctx->card->navigo->holder, 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#Environment:\n");
  118. show_opus_environment_info(
  119. &ctx->card->opus->environment, &ctx->card->opus->holder, 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#Environment:\n");
  129. show_ravkav_environment_info(&ctx->card->ravkav->environment, parsed_data);
  130. break;
  131. }
  132. default: {
  133. furi_string_cat_printf(parsed_data, "\e#Unknown %u:\n", ctx->card->card_number);
  134. furi_string_cat_printf(
  135. parsed_data, "Country: %s\n", get_country_string(ctx->card->country_num));
  136. if(guess_card_type(ctx->card->country_num, ctx->card->network_num) !=
  137. CALYPSO_CARD_UNKNOWN) {
  138. furi_string_cat_printf(
  139. parsed_data,
  140. "Network: %s\n",
  141. get_network_string(
  142. guess_card_type(ctx->card->country_num, ctx->card->network_num)));
  143. } else {
  144. furi_string_cat_printf(parsed_data, "Network: %d\n", ctx->card->network_num);
  145. }
  146. break;
  147. }
  148. }
  149. } else if(ctx->page_id == 1 || ctx->page_id == 2 || ctx->page_id == 3 || ctx->page_id == 4) {
  150. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id);
  151. switch(ctx->card->card_type) {
  152. case CALYPSO_CARD_NAVIGO: {
  153. show_navigo_contract_info(
  154. &ctx->card->navigo->contracts[ctx->page_id - 1], parsed_data);
  155. break;
  156. }
  157. case CALYPSO_CARD_OPUS: {
  158. show_opus_contract_info(&ctx->card->opus->contracts[ctx->page_id - 1], parsed_data);
  159. break;
  160. }
  161. case CALYPSO_CARD_RAVKAV: {
  162. show_ravkav_contract_info(
  163. &ctx->card->ravkav->contracts[ctx->page_id - 1], parsed_data);
  164. break;
  165. }
  166. default: {
  167. break;
  168. }
  169. }
  170. } else if(ctx->page_id >= 5) {
  171. if(ctx->page_id - 5 < ctx->card->events_count) {
  172. furi_string_cat_printf(parsed_data, "\e#Event %d:\n", ctx->page_id - 4);
  173. switch(ctx->card->card_type) {
  174. case CALYPSO_CARD_NAVIGO: {
  175. show_navigo_event_info(
  176. &ctx->card->navigo->events[ctx->page_id - 5],
  177. ctx->card->navigo->contracts,
  178. parsed_data);
  179. break;
  180. }
  181. case CALYPSO_CARD_OPUS: {
  182. show_opus_event_info(
  183. &ctx->card->opus->events[ctx->page_id - 5],
  184. ctx->card->opus->contracts,
  185. parsed_data);
  186. break;
  187. }
  188. case CALYPSO_CARD_RAVKAV: {
  189. show_ravkav_event_info(&ctx->card->ravkav->events[ctx->page_id - 5], parsed_data);
  190. break;
  191. }
  192. default: {
  193. break;
  194. }
  195. }
  196. } else {
  197. furi_string_cat_printf(
  198. parsed_data, "\e#Special Event %d:\n", ctx->page_id - ctx->card->events_count - 4);
  199. switch(ctx->card->card_type) {
  200. case CALYPSO_CARD_NAVIGO: {
  201. show_navigo_special_event_info(
  202. &ctx->card->navigo->special_events[ctx->page_id - ctx->card->events_count - 5],
  203. parsed_data);
  204. break;
  205. }
  206. case CALYPSO_CARD_OPUS: {
  207. break;
  208. }
  209. default: {
  210. break;
  211. }
  212. }
  213. }
  214. }
  215. }
  216. void update_widget_elements(void* context) {
  217. Metroflip* app = context;
  218. CalypsoContext* ctx = app->calypso_context;
  219. Widget* widget = app->widget;
  220. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO && ctx->card->card_type != CALYPSO_CARD_OPUS &&
  221. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  222. widget_add_button_element(
  223. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  224. return;
  225. }
  226. if(ctx->page_id < 10) {
  227. widget_add_button_element(
  228. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  229. } else {
  230. widget_add_button_element(
  231. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  232. }
  233. if(ctx->page_id > 0) {
  234. widget_add_button_element(
  235. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  236. }
  237. }
  238. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  239. Metroflip* app = context;
  240. CalypsoContext* ctx = app->calypso_context;
  241. UNUSED(result);
  242. Widget* widget = app->widget;
  243. if(type == InputTypePress) {
  244. widget_reset(widget);
  245. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  246. if(ctx->page_id > 0) {
  247. if(ctx->page_id == 10 && ctx->card->special_events_count < 2) {
  248. ctx->page_id -= 1;
  249. }
  250. if(ctx->page_id == 9 && ctx->card->special_events_count < 1) {
  251. ctx->page_id -= 1;
  252. }
  253. if(ctx->page_id == 8 && ctx->card->events_count < 3) {
  254. ctx->page_id -= 1;
  255. }
  256. if(ctx->page_id == 7 && ctx->card->events_count < 2) {
  257. ctx->page_id -= 1;
  258. }
  259. if(ctx->page_id == 6 && ctx->card->events_count < 1) {
  260. ctx->page_id -= 1;
  261. }
  262. if(ctx->page_id == 5 && ctx->card->contracts_count < 4) {
  263. ctx->page_id -= 1;
  264. }
  265. if(ctx->page_id == 4 && ctx->card->contracts_count < 3) {
  266. ctx->page_id -= 1;
  267. }
  268. if(ctx->page_id == 3 && ctx->card->contracts_count < 2) {
  269. ctx->page_id -= 1;
  270. }
  271. ctx->page_id -= 1;
  272. }
  273. FuriString* parsed_data = furi_string_alloc();
  274. // Ensure no nested mutexes
  275. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  276. update_page_info(app, parsed_data);
  277. furi_mutex_release(ctx->mutex);
  278. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  279. // widget_add_icon_element(widget, 0, 0, &I_RFIDDolphinReceive_97x61);
  280. // Ensure no nested mutexes
  281. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  282. update_widget_elements(app);
  283. furi_mutex_release(ctx->mutex);
  284. furi_string_free(parsed_data);
  285. }
  286. }
  287. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  288. Metroflip* app = context;
  289. CalypsoContext* ctx = app->calypso_context;
  290. UNUSED(result);
  291. Widget* widget = app->widget;
  292. if(type == InputTypePress) {
  293. widget_reset(widget);
  294. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  295. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO &&
  296. ctx->card->card_type != CALYPSO_CARD_OPUS &&
  297. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  298. ctx->page_id = 0;
  299. scene_manager_search_and_switch_to_previous_scene(
  300. app->scene_manager, MetroflipSceneStart);
  301. scene_manager_set_scene_state(
  302. app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
  303. return;
  304. }
  305. if(ctx->page_id < 10) {
  306. if(ctx->page_id == 1 && ctx->card->contracts_count < 2) {
  307. ctx->page_id += 1;
  308. }
  309. if(ctx->page_id == 2 && ctx->card->contracts_count < 3) {
  310. ctx->page_id += 1;
  311. }
  312. if(ctx->page_id == 3 && ctx->card->contracts_count < 4) {
  313. ctx->page_id += 1;
  314. }
  315. if(ctx->page_id == 4 && ctx->card->events_count < 1) {
  316. ctx->page_id += 1;
  317. }
  318. if(ctx->page_id == 5 && ctx->card->events_count < 2) {
  319. ctx->page_id += 1;
  320. }
  321. if(ctx->page_id == 6 && ctx->card->events_count < 3) {
  322. ctx->page_id += 1;
  323. }
  324. if(ctx->page_id == 7 && ctx->card->special_events_count < 1) {
  325. ctx->page_id += 1;
  326. }
  327. if(ctx->page_id == 8 && ctx->card->special_events_count < 2) {
  328. ctx->page_id += 1;
  329. }
  330. if(ctx->page_id == 9 && ctx->card->special_events_count < 3) {
  331. ctx->page_id = 0;
  332. scene_manager_search_and_switch_to_previous_scene(
  333. app->scene_manager, MetroflipSceneStart);
  334. scene_manager_set_scene_state(
  335. app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
  336. return;
  337. }
  338. ctx->page_id += 1;
  339. } else {
  340. ctx->page_id = 0;
  341. scene_manager_search_and_switch_to_previous_scene(
  342. app->scene_manager, MetroflipSceneStart);
  343. scene_manager_set_scene_state(
  344. app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
  345. return;
  346. }
  347. FuriString* parsed_data = furi_string_alloc();
  348. // Ensure no nested mutexes
  349. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  350. update_page_info(app, parsed_data);
  351. furi_mutex_release(ctx->mutex);
  352. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  353. // Ensure no nested mutexes
  354. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  355. update_widget_elements(app);
  356. furi_mutex_release(ctx->mutex);
  357. furi_string_free(parsed_data);
  358. }
  359. }
  360. void delay(int milliseconds) {
  361. furi_thread_flags_wait(0, FuriFlagWaitAny, milliseconds);
  362. }
  363. static NfcCommand calypso_poller_callback(NfcGenericEvent event, void* context) {
  364. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  365. NfcCommand next_command = NfcCommandContinue;
  366. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  367. Metroflip* app = context;
  368. FuriString* parsed_data = furi_string_alloc();
  369. Widget* widget = app->widget;
  370. furi_string_reset(app->text_box_store);
  371. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  372. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  373. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  374. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  375. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  376. if(stage == MetroflipPollerEventTypeStart) {
  377. // Start Flipper vibration
  378. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  379. notification_message(notification, &sequence_set_vibro_on);
  380. delay(50);
  381. notification_message(notification, &sequence_reset_vibro);
  382. nfc_device_set_data(
  383. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  384. Iso14443_4bError error;
  385. size_t response_length = 0;
  386. do {
  387. // Initialize the card data
  388. CalypsoCardData* card = malloc(sizeof(CalypsoCardData));
  389. // Select app ICC
  390. error = select_new_app(
  391. 0x00, 0x02, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  392. if(error != 0) {
  393. break;
  394. }
  395. // Check the response after selecting app
  396. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  397. break;
  398. }
  399. // Now send the read command for ICC
  400. error = read_new_file(0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  401. if(error != 0) {
  402. break;
  403. }
  404. // Check the response after reading the file
  405. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  406. break;
  407. }
  408. char icc_bit_representation[response_length * 8 + 1];
  409. icc_bit_representation[0] = '\0';
  410. for(size_t i = 0; i < response_length; i++) {
  411. char bits[9];
  412. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  413. byte_to_binary(byte, bits);
  414. strlcat(icc_bit_representation, bits, sizeof(icc_bit_representation));
  415. }
  416. icc_bit_representation[response_length * 8] = '\0';
  417. int start = 128, end = 159;
  418. card->card_number = bit_slice_to_dec(icc_bit_representation, start, end);
  419. // Select app for ticketing
  420. error = select_new_app(
  421. 0x20, 0x00, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  422. if(error != 0) {
  423. FURI_LOG_E(TAG, "Failed to select app for ticketing");
  424. break;
  425. }
  426. // Check the response after selecting app
  427. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  428. FURI_LOG_E(TAG, "Failed to check response after selecting app for ticketing");
  429. break;
  430. }
  431. // Select app for environment
  432. error = select_new_app(
  433. 0x20, 0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  434. if(error != 0) {
  435. break;
  436. }
  437. // Check the response after selecting app
  438. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  439. break;
  440. }
  441. // read file 1
  442. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  443. if(error != 0) {
  444. break;
  445. }
  446. // Check the response after reading the file
  447. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  448. break;
  449. }
  450. char environment_bit_representation[response_length * 8 + 1];
  451. environment_bit_representation[0] = '\0';
  452. for(size_t i = 0; i < response_length; i++) {
  453. char bits[9];
  454. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  455. byte_to_binary(byte, bits);
  456. strlcat(
  457. environment_bit_representation,
  458. bits,
  459. sizeof(environment_bit_representation));
  460. }
  461. // FURI_LOG_I(
  462. // TAG, "Environment bit_representation: %s", environment_bit_representation);
  463. start = 13;
  464. end = 16;
  465. card->country_num =
  466. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  467. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  468. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  469. start = 25;
  470. end = 28;
  471. card->network_num =
  472. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  473. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  474. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  475. card->card_type = guess_card_type(card->country_num, card->network_num);
  476. switch(card->card_type) {
  477. case CALYPSO_CARD_NAVIGO: {
  478. card->navigo = malloc(sizeof(NavigoCardData));
  479. card->navigo->environment.country_num = card->country_num;
  480. card->navigo->environment.network_num = card->network_num;
  481. CalypsoApp* IntercodeEnvHolderStructure = get_intercode_structure_env_holder();
  482. // EnvApplicationVersionNumber
  483. const char* env_key = "EnvApplicationVersionNumber";
  484. int positionOffset = get_calypso_node_offset(
  485. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  486. int start = positionOffset,
  487. end = positionOffset +
  488. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  489. card->navigo->environment.app_version =
  490. bit_slice_to_dec(environment_bit_representation, start, end);
  491. // EnvApplicationValidityEndDate
  492. env_key = "EnvApplicationValidityEndDate";
  493. positionOffset = get_calypso_node_offset(
  494. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  495. start = positionOffset,
  496. end = positionOffset +
  497. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  498. float decimal_value =
  499. bit_slice_to_dec(environment_bit_representation, start, end);
  500. uint64_t end_validity_timestamp =
  501. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  502. datetime_timestamp_to_datetime(
  503. end_validity_timestamp, &card->navigo->environment.end_dt);
  504. // HolderDataCardStatus
  505. env_key = "HolderDataCardStatus";
  506. positionOffset = get_calypso_node_offset(
  507. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  508. start = positionOffset,
  509. end = positionOffset +
  510. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  511. card->navigo->holder.card_status =
  512. bit_slice_to_dec(environment_bit_representation, start, end);
  513. // HolderDataCommercialID
  514. env_key = "HolderDataCommercialID";
  515. positionOffset = get_calypso_node_offset(
  516. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  517. start = positionOffset,
  518. end = positionOffset +
  519. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  520. card->navigo->holder.commercial_id =
  521. bit_slice_to_dec(environment_bit_representation, start, end);
  522. // Free the calypso structure
  523. free_calypso_structure(IntercodeEnvHolderStructure);
  524. // Select app for contracts
  525. error = select_new_app(
  526. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  527. if(error != 0) {
  528. FURI_LOG_E(TAG, "Failed to select app for contracts");
  529. break;
  530. }
  531. // Check the response after selecting app
  532. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  533. FURI_LOG_E(
  534. TAG, "Failed to check response after selecting app for contracts");
  535. break;
  536. }
  537. // Prepare calypso structure
  538. CalypsoApp* IntercodeContractStructure = get_intercode_structure_contract();
  539. if(!IntercodeContractStructure) {
  540. FURI_LOG_E(TAG, "Failed to load Intercode Contract structure");
  541. break;
  542. }
  543. // Now send the read command for contracts
  544. for(size_t i = 1; i < 5; i++) {
  545. error = read_new_file(
  546. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  547. if(error != 0) {
  548. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  549. break;
  550. }
  551. // Check the response after reading the file
  552. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  553. FURI_LOG_E(
  554. TAG, "Failed to check response after reading contract %d", i);
  555. break;
  556. }
  557. char bit_representation[response_length * 8 + 1];
  558. bit_representation[0] = '\0';
  559. for(size_t i = 0; i < response_length; i++) {
  560. char bits[9];
  561. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  562. byte_to_binary(byte, bits);
  563. strlcat(bit_representation, bits, sizeof(bit_representation));
  564. }
  565. bit_representation[response_length * 8] = '\0';
  566. if(bit_slice_to_dec(
  567. bit_representation,
  568. 0,
  569. IntercodeContractStructure->container->elements[0].bitmap->size -
  570. 1) == 0) {
  571. break;
  572. }
  573. card->navigo->contracts[i - 1].present = 1;
  574. card->contracts_count++;
  575. // 2. ContractTariff
  576. const char* contract_key = "ContractTariff";
  577. if(is_calypso_node_present(
  578. bit_representation, contract_key, IntercodeContractStructure)) {
  579. int positionOffset = get_calypso_node_offset(
  580. bit_representation, contract_key, IntercodeContractStructure);
  581. int start = positionOffset,
  582. end = positionOffset +
  583. get_calypso_node_size(
  584. contract_key, IntercodeContractStructure) -
  585. 1;
  586. card->navigo->contracts[i - 1].tariff =
  587. bit_slice_to_dec(bit_representation, start, end);
  588. }
  589. // 3. ContractSerialNumber
  590. contract_key = "ContractSerialNumber";
  591. if(is_calypso_node_present(
  592. bit_representation, contract_key, IntercodeContractStructure)) {
  593. int positionOffset = get_calypso_node_offset(
  594. bit_representation, contract_key, IntercodeContractStructure);
  595. int start = positionOffset,
  596. end = positionOffset +
  597. get_calypso_node_size(
  598. contract_key, IntercodeContractStructure) -
  599. 1;
  600. card->navigo->contracts[i - 1].serial_number =
  601. bit_slice_to_dec(bit_representation, start, end);
  602. card->navigo->contracts[i - 1].serial_number_available = true;
  603. }
  604. // 8. ContractPayMethod
  605. contract_key = "ContractPayMethod";
  606. if(is_calypso_node_present(
  607. bit_representation, contract_key, IntercodeContractStructure)) {
  608. int positionOffset = get_calypso_node_offset(
  609. bit_representation, contract_key, IntercodeContractStructure);
  610. int start = positionOffset,
  611. end = positionOffset +
  612. get_calypso_node_size(
  613. contract_key, IntercodeContractStructure) -
  614. 1;
  615. card->navigo->contracts[i - 1].pay_method =
  616. bit_slice_to_dec(bit_representation, start, end);
  617. card->navigo->contracts[i - 1].pay_method_available = true;
  618. }
  619. // 10. ContractPriceAmount
  620. contract_key = "ContractPriceAmount";
  621. if(is_calypso_node_present(
  622. bit_representation, contract_key, IntercodeContractStructure)) {
  623. int positionOffset = get_calypso_node_offset(
  624. bit_representation, contract_key, IntercodeContractStructure);
  625. int start = positionOffset,
  626. end = positionOffset +
  627. get_calypso_node_size(
  628. contract_key, IntercodeContractStructure) -
  629. 1;
  630. card->navigo->contracts[i - 1].price_amount =
  631. bit_slice_to_dec(bit_representation, start, end) / 100.0;
  632. card->navigo->contracts[i - 1].price_amount_available = true;
  633. }
  634. // 13.0. ContractValidityStartDate
  635. contract_key = "ContractValidityStartDate";
  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. float decimal_value =
  646. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  647. uint64_t start_validity_timestamp =
  648. (decimal_value + (float)epoch) + 3600;
  649. datetime_timestamp_to_datetime(
  650. start_validity_timestamp,
  651. &card->navigo->contracts[i - 1].start_date);
  652. }
  653. // 13.2. ContractValidityEndDate
  654. contract_key = "ContractValidityEndDate";
  655. if(is_calypso_node_present(
  656. bit_representation, contract_key, IntercodeContractStructure)) {
  657. int positionOffset = get_calypso_node_offset(
  658. bit_representation, contract_key, IntercodeContractStructure);
  659. int start = positionOffset,
  660. end = positionOffset +
  661. get_calypso_node_size(
  662. contract_key, IntercodeContractStructure) -
  663. 1;
  664. float decimal_value =
  665. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  666. uint64_t end_validity_timestamp =
  667. (decimal_value + (float)epoch) + 3600;
  668. datetime_timestamp_to_datetime(
  669. end_validity_timestamp, &card->navigo->contracts[i - 1].end_date);
  670. card->navigo->contracts[i - 1].end_date_available = true;
  671. }
  672. // 13.6. ContractValidityZones
  673. contract_key = "ContractValidityZones";
  674. if(is_calypso_node_present(
  675. bit_representation, contract_key, IntercodeContractStructure)) {
  676. int start = get_calypso_node_offset(
  677. bit_representation, contract_key, IntercodeContractStructure);
  678. // binary form is 00011111 for zones 5, 4, 3, 2, 1
  679. for(int j = 0; j < 5; j++) {
  680. card->navigo->contracts[i - 1].zones[j] = bit_slice_to_dec(
  681. bit_representation, start + 3 + j, start + 3 + j);
  682. }
  683. card->navigo->contracts[i - 1].zones_available = true;
  684. }
  685. // 13.7. ContractValidityJourneys
  686. contract_key = "ContractValidityJourneys";
  687. if(is_calypso_node_present(
  688. bit_representation, contract_key, IntercodeContractStructure)) {
  689. int positionOffset = get_calypso_node_offset(
  690. bit_representation, contract_key, IntercodeContractStructure);
  691. int start = positionOffset,
  692. end = positionOffset +
  693. get_calypso_node_size(
  694. contract_key, IntercodeContractStructure) -
  695. 1;
  696. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  697. // first 5 bits -> CounterStructureNumber
  698. // last 8 bits -> CounterLastLoad
  699. // other bits -> RFU
  700. card->navigo->contracts[i - 1].counter.struct_number = decimal_value >>
  701. 11;
  702. card->navigo->contracts[i - 1].counter.last_load = decimal_value &
  703. 0xFF;
  704. card->navigo->contracts[i - 1].counter_present = true;
  705. }
  706. // 15.0. ContractValiditySaleDate
  707. contract_key = "ContractValiditySaleDate";
  708. if(is_calypso_node_present(
  709. bit_representation, contract_key, IntercodeContractStructure)) {
  710. int positionOffset = get_calypso_node_offset(
  711. bit_representation, contract_key, IntercodeContractStructure);
  712. int start = positionOffset,
  713. end = positionOffset +
  714. get_calypso_node_size(
  715. contract_key, IntercodeContractStructure) -
  716. 1;
  717. float decimal_value =
  718. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  719. uint64_t sale_timestamp = (decimal_value + (float)epoch) + 3600;
  720. datetime_timestamp_to_datetime(
  721. sale_timestamp, &card->navigo->contracts[i - 1].sale_date);
  722. }
  723. // 15.2. ContractValiditySaleAgent - FIX NEEDED
  724. contract_key = "ContractValiditySaleAgent";
  725. /* if(is_calypso_node_present(
  726. bit_representation, contract_key, NavigoContractStructure)) { */
  727. int positionOffset = get_calypso_node_offset(
  728. bit_representation, contract_key, IntercodeContractStructure);
  729. int start = positionOffset,
  730. end = positionOffset +
  731. get_calypso_node_size(contract_key, IntercodeContractStructure) -
  732. 1;
  733. card->navigo->contracts[i - 1].sale_agent =
  734. bit_slice_to_dec(bit_representation, start, end);
  735. // }
  736. // 15.3. ContractValiditySaleDevice
  737. contract_key = "ContractValiditySaleDevice";
  738. if(is_calypso_node_present(
  739. bit_representation, contract_key, IntercodeContractStructure)) {
  740. int positionOffset = get_calypso_node_offset(
  741. bit_representation, contract_key, IntercodeContractStructure);
  742. int start = positionOffset,
  743. end = positionOffset +
  744. get_calypso_node_size(
  745. contract_key, IntercodeContractStructure) -
  746. 1;
  747. card->navigo->contracts[i - 1].sale_device =
  748. bit_slice_to_dec(bit_representation, start, end);
  749. }
  750. // 16. ContractStatus -- 0x1 ou 0xff
  751. contract_key = "ContractStatus";
  752. if(is_calypso_node_present(
  753. bit_representation, contract_key, IntercodeContractStructure)) {
  754. int positionOffset = get_calypso_node_offset(
  755. bit_representation, contract_key, IntercodeContractStructure);
  756. int start = positionOffset,
  757. end = positionOffset +
  758. get_calypso_node_size(
  759. contract_key, IntercodeContractStructure) -
  760. 1;
  761. card->navigo->contracts[i - 1].status =
  762. bit_slice_to_dec(bit_representation, start, end);
  763. }
  764. // 18. ContractAuthenticator
  765. contract_key = "ContractAuthenticator";
  766. if(is_calypso_node_present(
  767. bit_representation, contract_key, IntercodeContractStructure)) {
  768. int positionOffset = get_calypso_node_offset(
  769. bit_representation, contract_key, IntercodeContractStructure);
  770. int start = positionOffset,
  771. end = positionOffset +
  772. get_calypso_node_size(
  773. contract_key, IntercodeContractStructure) -
  774. 1;
  775. card->navigo->contracts[i - 1].authenticator =
  776. bit_slice_to_dec(bit_representation, start, end);
  777. }
  778. }
  779. // Free the calypso structure
  780. free_calypso_structure(IntercodeContractStructure);
  781. // Select app for counters (remaining tickets on Navigo Easy)
  782. error = select_new_app(
  783. 0x20, 0x69, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  784. if(error != 0) {
  785. break;
  786. }
  787. // Check the response after selecting app
  788. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  789. break;
  790. }
  791. // read file 1
  792. error =
  793. read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  794. if(error != 0) {
  795. break;
  796. }
  797. // Check the response after reading the file
  798. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  799. break;
  800. }
  801. char counter_bit_representation[response_length * 8 + 1];
  802. counter_bit_representation[0] = '\0';
  803. for(size_t i = 0; i < response_length; i++) {
  804. char bits[9];
  805. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  806. byte_to_binary(byte, bits);
  807. strlcat(
  808. counter_bit_representation, bits, sizeof(counter_bit_representation));
  809. }
  810. // FURI_LOG_I(TAG, "Counter bit_representation: %s", counter_bit_representation);
  811. // Ticket counts (contracts 1-4)
  812. for(int i = 0; i < 4; i++) {
  813. if(card->navigo->contracts[i].present == 0) {
  814. continue;
  815. }
  816. if(card->navigo->contracts[i].counter_present == 0) {
  817. continue;
  818. }
  819. start = 0;
  820. end = 5;
  821. card->navigo->contracts[i].counter.count = bit_slice_to_dec(
  822. counter_bit_representation, 24 * i + start, 24 * i + end);
  823. start = 6;
  824. end = 23;
  825. card->navigo->contracts[i].counter.relative_first_stamp_15mn =
  826. bit_slice_to_dec(
  827. counter_bit_representation, 24 * i + start, 24 * i + end);
  828. }
  829. // Select app for events
  830. error = select_new_app(
  831. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  832. if(error != 0) {
  833. break;
  834. }
  835. // Check the response after selecting app
  836. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  837. break;
  838. }
  839. // Load the calypso structure for events
  840. CalypsoApp* IntercodeEventStructure = get_intercode_structure_event();
  841. if(!IntercodeEventStructure) {
  842. FURI_LOG_E(TAG, "Failed to load Intercode Event structure");
  843. break;
  844. }
  845. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  846. // Now send the read command for events
  847. for(size_t i = 1; i < 4; i++) {
  848. error = read_new_file(
  849. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  850. if(error != 0) {
  851. break;
  852. }
  853. // Check the response after reading the file
  854. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  855. break;
  856. }
  857. char event_bit_representation[response_length * 8 + 1];
  858. event_bit_representation[0] = '\0';
  859. for(size_t i = 0; i < response_length; i++) {
  860. char bits[9];
  861. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  862. byte_to_binary(byte, bits);
  863. strlcat(
  864. event_bit_representation, bits, sizeof(event_bit_representation));
  865. }
  866. // 2. EventCode
  867. const char* event_key = "EventCode";
  868. if(is_calypso_node_present(
  869. event_bit_representation, event_key, IntercodeEventStructure)) {
  870. int positionOffset = get_calypso_node_offset(
  871. event_bit_representation, event_key, IntercodeEventStructure);
  872. int start = positionOffset,
  873. end = positionOffset +
  874. get_calypso_node_size(event_key, IntercodeEventStructure) -
  875. 1;
  876. int decimal_value =
  877. bit_slice_to_dec(event_bit_representation, start, end);
  878. card->navigo->events[i - 1].transport_type = decimal_value >> 4;
  879. card->navigo->events[i - 1].transition = decimal_value & 15;
  880. }
  881. // 4. EventServiceProvider
  882. event_key = "EventServiceProvider";
  883. if(is_calypso_node_present(
  884. event_bit_representation, event_key, IntercodeEventStructure)) {
  885. int positionOffset = get_calypso_node_offset(
  886. event_bit_representation, event_key, IntercodeEventStructure);
  887. start = positionOffset,
  888. end = positionOffset +
  889. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  890. card->navigo->events[i - 1].service_provider =
  891. bit_slice_to_dec(event_bit_representation, start, end);
  892. }
  893. // 8. EventLocationId
  894. event_key = "EventLocationId";
  895. if(is_calypso_node_present(
  896. event_bit_representation, event_key, IntercodeEventStructure)) {
  897. int positionOffset = get_calypso_node_offset(
  898. event_bit_representation, event_key, IntercodeEventStructure);
  899. start = positionOffset,
  900. end = positionOffset +
  901. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  902. int decimal_value =
  903. bit_slice_to_dec(event_bit_representation, start, end);
  904. card->navigo->events[i - 1].station_group_id = decimal_value >> 9;
  905. card->navigo->events[i - 1].station_id = (decimal_value >> 4) & 31;
  906. card->navigo->events[i - 1].station_sub_id = decimal_value & 15;
  907. }
  908. // 9. EventLocationGate
  909. event_key = "EventLocationGate";
  910. if(is_calypso_node_present(
  911. event_bit_representation, event_key, IntercodeEventStructure)) {
  912. int 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. card->navigo->events[i - 1].location_gate =
  918. bit_slice_to_dec(event_bit_representation, start, end);
  919. card->navigo->events[i - 1].location_gate_available = true;
  920. }
  921. // 10. EventDevice
  922. event_key = "EventDevice";
  923. if(is_calypso_node_present(
  924. event_bit_representation, event_key, IntercodeEventStructure)) {
  925. int positionOffset = get_calypso_node_offset(
  926. event_bit_representation, event_key, IntercodeEventStructure);
  927. start = positionOffset,
  928. end = positionOffset +
  929. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  930. int decimal_value =
  931. bit_slice_to_dec(event_bit_representation, start, end);
  932. card->navigo->events[i - 1].device = decimal_value;
  933. int bus_device = decimal_value >> 8;
  934. card->navigo->events[i - 1].door = bus_device / 2 + 1;
  935. card->navigo->events[i - 1].side = bus_device % 2;
  936. card->navigo->events[i - 1].device_available = true;
  937. }
  938. // 11. EventRouteNumber
  939. event_key = "EventRouteNumber";
  940. if(is_calypso_node_present(
  941. event_bit_representation, event_key, IntercodeEventStructure)) {
  942. int positionOffset = get_calypso_node_offset(
  943. event_bit_representation, event_key, IntercodeEventStructure);
  944. start = positionOffset,
  945. end = positionOffset +
  946. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  947. card->navigo->events[i - 1].route_number =
  948. bit_slice_to_dec(event_bit_representation, start, end);
  949. card->navigo->events[i - 1].route_number_available = true;
  950. }
  951. // 13. EventJourneyRun
  952. event_key = "EventJourneyRun";
  953. if(is_calypso_node_present(
  954. event_bit_representation, event_key, IntercodeEventStructure)) {
  955. int positionOffset = get_calypso_node_offset(
  956. event_bit_representation, event_key, IntercodeEventStructure);
  957. start = positionOffset,
  958. end = positionOffset +
  959. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  960. card->navigo->events[i - 1].mission =
  961. bit_slice_to_dec(event_bit_representation, start, end);
  962. card->navigo->events[i - 1].mission_available = true;
  963. }
  964. // 14. EventVehicleId
  965. event_key = "EventVehicleId";
  966. if(is_calypso_node_present(
  967. event_bit_representation, event_key, IntercodeEventStructure)) {
  968. int positionOffset = get_calypso_node_offset(
  969. event_bit_representation, event_key, IntercodeEventStructure);
  970. start = positionOffset,
  971. end = positionOffset +
  972. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  973. card->navigo->events[i - 1].vehicle_id =
  974. bit_slice_to_dec(event_bit_representation, start, end);
  975. card->navigo->events[i - 1].vehicle_id_available = true;
  976. }
  977. // 25. EventContractPointer
  978. event_key = "EventContractPointer";
  979. if(is_calypso_node_present(
  980. event_bit_representation, event_key, IntercodeEventStructure)) {
  981. int positionOffset = get_calypso_node_offset(
  982. event_bit_representation, event_key, IntercodeEventStructure);
  983. start = positionOffset,
  984. end = positionOffset +
  985. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  986. card->navigo->events[i - 1].used_contract =
  987. bit_slice_to_dec(event_bit_representation, start, end);
  988. card->navigo->events[i - 1].used_contract_available = true;
  989. if(card->navigo->events[i - 1].used_contract > 0) {
  990. card->events_count++;
  991. }
  992. }
  993. // EventDateStamp
  994. event_key = "EventDateStamp";
  995. int positionOffset = get_calypso_node_offset(
  996. event_bit_representation, event_key, IntercodeEventStructure);
  997. start = positionOffset,
  998. end = positionOffset +
  999. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1000. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1001. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1002. datetime_timestamp_to_datetime(
  1003. date_timestamp, &card->navigo->events[i - 1].date);
  1004. // EventTimeStamp
  1005. event_key = "EventTimeStamp";
  1006. positionOffset = get_calypso_node_offset(
  1007. event_bit_representation, event_key, IntercodeEventStructure);
  1008. start = positionOffset,
  1009. end = positionOffset +
  1010. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1011. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1012. card->navigo->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1013. card->navigo->events[i - 1].date.minute =
  1014. ((decimal_value * 60) % 3600) / 60;
  1015. card->navigo->events[i - 1].date.second =
  1016. ((decimal_value * 60) % 3600) % 60;
  1017. }
  1018. // Select app for special events
  1019. error = select_new_app(
  1020. 0x20, 0x40, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1021. if(error != 0) {
  1022. break;
  1023. }
  1024. // Check the response after selecting app
  1025. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1026. break;
  1027. }
  1028. // Now send the read command for special events
  1029. for(size_t i = 1; i < 4; i++) {
  1030. error = read_new_file(
  1031. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1032. if(error != 0) {
  1033. break;
  1034. }
  1035. // Check the response after reading the file
  1036. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1037. break;
  1038. }
  1039. char event_bit_representation[response_length * 8 + 1];
  1040. event_bit_representation[0] = '\0';
  1041. for(size_t i = 0; i < response_length; i++) {
  1042. char bits[9];
  1043. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1044. byte_to_binary(byte, bits);
  1045. strlcat(
  1046. event_bit_representation, bits, sizeof(event_bit_representation));
  1047. }
  1048. if(bit_slice_to_dec(
  1049. event_bit_representation,
  1050. 0,
  1051. IntercodeEventStructure->container->elements[0].bitmap->size - 1) ==
  1052. 0) {
  1053. break;
  1054. } else {
  1055. card->special_events_count++;
  1056. }
  1057. // 2. EventCode
  1058. const char* event_key = "EventCode";
  1059. if(is_calypso_node_present(
  1060. event_bit_representation, event_key, IntercodeEventStructure)) {
  1061. int positionOffset = get_calypso_node_offset(
  1062. event_bit_representation, event_key, IntercodeEventStructure);
  1063. int start = positionOffset,
  1064. end = positionOffset +
  1065. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1066. 1;
  1067. int decimal_value =
  1068. bit_slice_to_dec(event_bit_representation, start, end);
  1069. card->navigo->special_events[i - 1].transport_type = decimal_value >>
  1070. 4;
  1071. card->navigo->special_events[i - 1].transition = decimal_value & 15;
  1072. }
  1073. // 3. EventResult
  1074. event_key = "EventResult";
  1075. if(is_calypso_node_present(
  1076. event_bit_representation, event_key, IntercodeEventStructure)) {
  1077. int positionOffset = get_calypso_node_offset(
  1078. event_bit_representation, event_key, IntercodeEventStructure);
  1079. int start = positionOffset,
  1080. end = positionOffset +
  1081. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1082. 1;
  1083. card->navigo->special_events[i - 1].result =
  1084. bit_slice_to_dec(event_bit_representation, start, end);
  1085. }
  1086. // 4. EventServiceProvider
  1087. event_key = "EventServiceProvider";
  1088. if(is_calypso_node_present(
  1089. event_bit_representation, event_key, IntercodeEventStructure)) {
  1090. int positionOffset = get_calypso_node_offset(
  1091. event_bit_representation, event_key, IntercodeEventStructure);
  1092. int start = positionOffset,
  1093. end = positionOffset +
  1094. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1095. 1;
  1096. card->navigo->special_events[i - 1].service_provider =
  1097. bit_slice_to_dec(event_bit_representation, start, end);
  1098. }
  1099. // 8. EventLocationId
  1100. event_key = "EventLocationId";
  1101. if(is_calypso_node_present(
  1102. event_bit_representation, event_key, IntercodeEventStructure)) {
  1103. int positionOffset = get_calypso_node_offset(
  1104. event_bit_representation, event_key, IntercodeEventStructure);
  1105. int start = positionOffset,
  1106. end = positionOffset +
  1107. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1108. 1;
  1109. int decimal_value =
  1110. bit_slice_to_dec(event_bit_representation, start, end);
  1111. card->navigo->special_events[i - 1].station_group_id = decimal_value >>
  1112. 9;
  1113. card->navigo->special_events[i - 1].station_id = (decimal_value >> 4) &
  1114. 31;
  1115. card->navigo->special_events[i - 1].station_sub_id = decimal_value &
  1116. 15;
  1117. }
  1118. // 10. EventDevice
  1119. event_key = "EventDevice";
  1120. if(is_calypso_node_present(
  1121. event_bit_representation, event_key, IntercodeEventStructure)) {
  1122. int positionOffset = get_calypso_node_offset(
  1123. event_bit_representation, event_key, IntercodeEventStructure);
  1124. int start = positionOffset,
  1125. end = positionOffset +
  1126. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1127. 1;
  1128. int decimal_value =
  1129. bit_slice_to_dec(event_bit_representation, start, end);
  1130. card->navigo->special_events[i - 1].device = decimal_value;
  1131. }
  1132. // 11. EventRouteNumber
  1133. event_key = "EventRouteNumber";
  1134. if(is_calypso_node_present(
  1135. event_bit_representation, event_key, IntercodeEventStructure)) {
  1136. int positionOffset = get_calypso_node_offset(
  1137. event_bit_representation, event_key, IntercodeEventStructure);
  1138. int start = positionOffset,
  1139. end = positionOffset +
  1140. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1141. 1;
  1142. card->navigo->special_events[i - 1].route_number =
  1143. bit_slice_to_dec(event_bit_representation, start, end);
  1144. card->navigo->special_events[i - 1].route_number_available = true;
  1145. }
  1146. // EventDateStamp
  1147. event_key = "EventDateStamp";
  1148. int positionOffset = get_calypso_node_offset(
  1149. event_bit_representation, event_key, IntercodeEventStructure);
  1150. int start = positionOffset,
  1151. end = positionOffset +
  1152. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1153. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1154. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1155. datetime_timestamp_to_datetime(
  1156. date_timestamp, &card->navigo->special_events[i - 1].date);
  1157. // EventTimeStamp
  1158. event_key = "EventTimeStamp";
  1159. positionOffset = get_calypso_node_offset(
  1160. event_bit_representation, event_key, IntercodeEventStructure);
  1161. start = positionOffset,
  1162. end = positionOffset +
  1163. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1164. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1165. card->navigo->special_events[i - 1].date.hour =
  1166. (decimal_value * 60) / 3600;
  1167. card->navigo->special_events[i - 1].date.minute =
  1168. ((decimal_value * 60) % 3600) / 60;
  1169. card->navigo->special_events[i - 1].date.second =
  1170. ((decimal_value * 60) % 3600) % 60;
  1171. }
  1172. // Free the calypso structure
  1173. free_calypso_structure(IntercodeEventStructure);
  1174. break;
  1175. }
  1176. case CALYPSO_CARD_OPUS: {
  1177. card->opus = malloc(sizeof(OpusCardData));
  1178. card->opus->environment.country_num = card->country_num;
  1179. card->opus->environment.network_num = card->network_num;
  1180. CalypsoApp* OpusEnvHolderStructure = get_opus_env_holder_structure();
  1181. // EnvApplicationVersionNumber
  1182. const char* env_key = "EnvApplicationVersionNumber";
  1183. int positionOffset = get_calypso_node_offset(
  1184. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1185. int start = positionOffset,
  1186. end = positionOffset +
  1187. get_calypso_node_size(env_key, OpusEnvHolderStructure) - 1;
  1188. card->opus->environment.app_version =
  1189. bit_slice_to_dec(environment_bit_representation, start, end);
  1190. // EnvApplicationIssuerId
  1191. env_key = "EnvApplicationIssuerId";
  1192. positionOffset = get_calypso_node_offset(
  1193. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1194. start = positionOffset,
  1195. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1196. 1;
  1197. card->opus->environment.issuer_id =
  1198. bit_slice_to_dec(environment_bit_representation, start, end);
  1199. // EnvApplicationValidityEndDate
  1200. env_key = "EnvApplicationValidityEndDate";
  1201. positionOffset = get_calypso_node_offset(
  1202. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1203. start = positionOffset,
  1204. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1205. 1;
  1206. float decimal_value =
  1207. bit_slice_to_dec(environment_bit_representation, start, end);
  1208. uint64_t end_validity_timestamp =
  1209. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  1210. datetime_timestamp_to_datetime(
  1211. end_validity_timestamp, &card->opus->environment.end_dt);
  1212. // EnvDataCardStatus
  1213. env_key = "EnvDataCardStatus";
  1214. positionOffset = get_calypso_node_offset(
  1215. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1216. start = positionOffset,
  1217. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1218. 1;
  1219. card->opus->environment.card_status =
  1220. bit_slice_to_dec(environment_bit_representation, start, end);
  1221. // EnvData_CardUtilisation
  1222. env_key = "EnvData_CardUtilisation";
  1223. positionOffset = get_calypso_node_offset(
  1224. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1225. start = positionOffset,
  1226. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1227. 1;
  1228. card->opus->environment.card_utilisation =
  1229. bit_slice_to_dec(environment_bit_representation, start, end);
  1230. // HolderBirthDate
  1231. env_key = "HolderBirthDate";
  1232. positionOffset = get_calypso_node_offset(
  1233. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1234. start = positionOffset, end = positionOffset + 3;
  1235. card->opus->holder.birth_date.year =
  1236. bit_slice_to_dec(environment_bit_representation, start, end) * 1000 +
  1237. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) *
  1238. 100 +
  1239. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8) * 10 +
  1240. bit_slice_to_dec(environment_bit_representation, start + 12, end + 12);
  1241. start += 16, end += 16;
  1242. card->opus->holder.birth_date.month =
  1243. bit_slice_to_dec(environment_bit_representation, start, end) * 10 +
  1244. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4);
  1245. start += 8, end += 8;
  1246. card->opus->holder.birth_date.day =
  1247. bit_slice_to_dec(environment_bit_representation, start, end) * 10 +
  1248. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4);
  1249. // Free the calypso structure
  1250. free_calypso_structure(OpusEnvHolderStructure);
  1251. // Select app for contracts
  1252. error = select_new_app(
  1253. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1254. if(error != 0) {
  1255. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1256. break;
  1257. }
  1258. // Check the response after selecting app
  1259. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1260. FURI_LOG_E(
  1261. TAG, "Failed to check response after selecting app for contracts");
  1262. break;
  1263. }
  1264. // Prepare calypso structure
  1265. CalypsoApp* OpusContractStructure = get_opus_contract_structure();
  1266. if(!OpusContractStructure) {
  1267. FURI_LOG_E(TAG, "Failed to load Opus Contract structure");
  1268. break;
  1269. }
  1270. // Now send the read command for contracts
  1271. for(size_t i = 1; i < 5; i++) {
  1272. error = read_new_file(
  1273. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1274. if(error != 0) {
  1275. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1276. break;
  1277. }
  1278. // Check the response after reading the file
  1279. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1280. FURI_LOG_E(
  1281. TAG, "Failed to check response after reading contract %d", i);
  1282. break;
  1283. }
  1284. char bit_representation[response_length * 8 + 1];
  1285. bit_representation[0] = '\0';
  1286. for(size_t i = 0; i < response_length; i++) {
  1287. char bits[9];
  1288. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1289. byte_to_binary(byte, bits);
  1290. strlcat(bit_representation, bits, sizeof(bit_representation));
  1291. }
  1292. bit_representation[response_length * 8] = '\0';
  1293. if(bit_slice_to_dec(
  1294. bit_representation,
  1295. 0,
  1296. OpusContractStructure->container->elements[0].bitmap->size - 1) ==
  1297. 0) {
  1298. break;
  1299. }
  1300. card->opus->contracts[i - 1].present = 1;
  1301. card->contracts_count++;
  1302. // ContractProvider
  1303. const char* contract_key = "ContractProvider";
  1304. if(is_calypso_node_present(
  1305. bit_representation, contract_key, OpusContractStructure)) {
  1306. int positionOffset = get_calypso_node_offset(
  1307. bit_representation, contract_key, OpusContractStructure);
  1308. int start = positionOffset,
  1309. end = positionOffset +
  1310. get_calypso_node_size(contract_key, OpusContractStructure) -
  1311. 1;
  1312. card->opus->contracts[i - 1].provider =
  1313. bit_slice_to_dec(bit_representation, start, end);
  1314. }
  1315. // ContractTariff
  1316. contract_key = "ContractTariff";
  1317. if(is_calypso_node_present(
  1318. bit_representation, contract_key, OpusContractStructure)) {
  1319. int positionOffset = get_calypso_node_offset(
  1320. bit_representation, contract_key, OpusContractStructure);
  1321. int start = positionOffset,
  1322. end = positionOffset +
  1323. get_calypso_node_size(contract_key, OpusContractStructure) -
  1324. 1;
  1325. card->opus->contracts[i - 1].tariff =
  1326. bit_slice_to_dec(bit_representation, start, end);
  1327. }
  1328. // ContractValidityStartDate
  1329. contract_key = "ContractValidityStartDate";
  1330. if(is_calypso_node_present(
  1331. bit_representation, contract_key, OpusContractStructure)) {
  1332. int positionOffset = get_calypso_node_offset(
  1333. bit_representation, contract_key, OpusContractStructure);
  1334. int start = positionOffset,
  1335. end = positionOffset +
  1336. get_calypso_node_size(contract_key, OpusContractStructure) -
  1337. 1;
  1338. float decimal_value =
  1339. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1340. uint64_t start_validity_timestamp =
  1341. (decimal_value + (float)epoch) + 3600;
  1342. datetime_timestamp_to_datetime(
  1343. start_validity_timestamp,
  1344. &card->opus->contracts[i - 1].start_date);
  1345. }
  1346. // ContractValidityEndDate
  1347. contract_key = "ContractValidityEndDate";
  1348. if(is_calypso_node_present(
  1349. bit_representation, contract_key, OpusContractStructure)) {
  1350. int positionOffset = get_calypso_node_offset(
  1351. bit_representation, contract_key, OpusContractStructure);
  1352. int start = positionOffset,
  1353. end = positionOffset +
  1354. get_calypso_node_size(contract_key, OpusContractStructure) -
  1355. 1;
  1356. float decimal_value =
  1357. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1358. uint64_t end_validity_timestamp =
  1359. (decimal_value + (float)epoch) + 3600;
  1360. datetime_timestamp_to_datetime(
  1361. end_validity_timestamp, &card->opus->contracts[i - 1].end_date);
  1362. }
  1363. // ContractDataSaleAgent
  1364. contract_key = "ContractDataSaleAgent";
  1365. if(is_calypso_node_present(
  1366. bit_representation, contract_key, OpusContractStructure)) {
  1367. int positionOffset = get_calypso_node_offset(
  1368. bit_representation, contract_key, OpusContractStructure);
  1369. int start = positionOffset,
  1370. end = positionOffset +
  1371. get_calypso_node_size(contract_key, OpusContractStructure) -
  1372. 1;
  1373. card->opus->contracts[i - 1].sale_agent =
  1374. bit_slice_to_dec(bit_representation, start, end);
  1375. }
  1376. // ContractDataSaleDate + ContractDataSaleTime
  1377. contract_key = "ContractDataSaleDate";
  1378. int positionOffset = get_calypso_node_offset(
  1379. bit_representation, contract_key, OpusContractStructure);
  1380. FURI_LOG_I(TAG, "ContractDataSaleDate positionOffset: %d", positionOffset);
  1381. int start = positionOffset,
  1382. end = positionOffset +
  1383. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1384. FURI_LOG_I(
  1385. TAG,
  1386. "ContractDataSaleDate: %d",
  1387. bit_slice_to_dec(bit_representation, start, end));
  1388. uint64_t sale_date_timestamp =
  1389. ((bit_slice_to_dec(bit_representation, start, end) * 24 * 3600) +
  1390. (float)epoch) +
  1391. 3600;
  1392. ;
  1393. datetime_timestamp_to_datetime(
  1394. sale_date_timestamp, &card->opus->contracts[i - 1].sale_date);
  1395. contract_key = "ContractDataSaleTime";
  1396. positionOffset = get_calypso_node_offset(
  1397. bit_representation, contract_key, OpusContractStructure);
  1398. start = positionOffset,
  1399. end = positionOffset +
  1400. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1401. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  1402. card->opus->contracts[i - 1].sale_date.hour = (decimal_value * 60) / 3600;
  1403. card->opus->contracts[i - 1].sale_date.minute =
  1404. ((decimal_value * 60) % 3600) / 60;
  1405. card->opus->contracts[i - 1].sale_date.second =
  1406. ((decimal_value * 60) % 3600) % 60;
  1407. // ContractDataInhibition
  1408. contract_key = "ContractDataInhibition";
  1409. if(is_calypso_node_present(
  1410. bit_representation, contract_key, OpusContractStructure)) {
  1411. int positionOffset = get_calypso_node_offset(
  1412. bit_representation, contract_key, OpusContractStructure);
  1413. int start = positionOffset,
  1414. end = positionOffset +
  1415. get_calypso_node_size(contract_key, OpusContractStructure) -
  1416. 1;
  1417. card->opus->contracts[i - 1].inhibition =
  1418. bit_slice_to_dec(bit_representation, start, end);
  1419. }
  1420. // ContractDataUsed
  1421. contract_key = "ContractDataUsed";
  1422. if(is_calypso_node_present(
  1423. bit_representation, contract_key, OpusContractStructure)) {
  1424. int positionOffset = get_calypso_node_offset(
  1425. bit_representation, contract_key, OpusContractStructure);
  1426. int start = positionOffset,
  1427. end = positionOffset +
  1428. get_calypso_node_size(contract_key, OpusContractStructure) -
  1429. 1;
  1430. card->opus->contracts[i - 1].used =
  1431. bit_slice_to_dec(bit_representation, start, end);
  1432. }
  1433. }
  1434. // Free the calypso structure
  1435. free_calypso_structure(OpusContractStructure);
  1436. // Select app for events
  1437. error = select_new_app(
  1438. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1439. if(error != 0) {
  1440. break;
  1441. }
  1442. // Check the response after selecting app
  1443. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1444. break;
  1445. }
  1446. // Load the calypso structure for events
  1447. CalypsoApp* OpusEventStructure = get_opus_event_structure();
  1448. if(!OpusEventStructure) {
  1449. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1450. break;
  1451. }
  1452. // Now send the read command for events
  1453. for(size_t i = 1; i < 4; i++) {
  1454. error = read_new_file(
  1455. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1456. if(error != 0) {
  1457. break;
  1458. }
  1459. // Check the response after reading the file
  1460. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1461. break;
  1462. }
  1463. char event_bit_representation[response_length * 8 + 1];
  1464. event_bit_representation[0] = '\0';
  1465. for(size_t i = 0; i < response_length; i++) {
  1466. char bits[9];
  1467. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1468. byte_to_binary(byte, bits);
  1469. strlcat(
  1470. event_bit_representation, bits, sizeof(event_bit_representation));
  1471. }
  1472. // EventResult
  1473. const char* event_key = "EventResult";
  1474. if(is_calypso_node_present(
  1475. event_bit_representation, event_key, OpusEventStructure)) {
  1476. int positionOffset = get_calypso_node_offset(
  1477. event_bit_representation, event_key, OpusEventStructure);
  1478. int start = positionOffset,
  1479. end = positionOffset +
  1480. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1481. card->opus->events[i - 1].result =
  1482. bit_slice_to_dec(event_bit_representation, start, end);
  1483. }
  1484. // EventServiceProvider
  1485. event_key = "EventServiceProvider";
  1486. if(is_calypso_node_present(
  1487. event_bit_representation, event_key, OpusEventStructure)) {
  1488. int positionOffset = get_calypso_node_offset(
  1489. event_bit_representation, event_key, OpusEventStructure);
  1490. int start = positionOffset,
  1491. end = positionOffset +
  1492. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1493. card->opus->events[i - 1].service_provider =
  1494. bit_slice_to_dec(event_bit_representation, start, end);
  1495. }
  1496. // EventLocationId
  1497. event_key = "EventLocationId";
  1498. if(is_calypso_node_present(
  1499. event_bit_representation, event_key, OpusEventStructure)) {
  1500. int positionOffset = get_calypso_node_offset(
  1501. event_bit_representation, event_key, OpusEventStructure);
  1502. int start = positionOffset,
  1503. end = positionOffset +
  1504. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1505. card->opus->events[i - 1].location_id =
  1506. bit_slice_to_dec(event_bit_representation, start, end);
  1507. }
  1508. // EventRouteNumber
  1509. event_key = "EventRouteNumber";
  1510. if(is_calypso_node_present(
  1511. event_bit_representation, event_key, OpusEventStructure)) {
  1512. int positionOffset = get_calypso_node_offset(
  1513. event_bit_representation, event_key, OpusEventStructure);
  1514. int start = positionOffset,
  1515. end = positionOffset +
  1516. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1517. card->opus->events[i - 1].route_number =
  1518. bit_slice_to_dec(event_bit_representation, start, end);
  1519. }
  1520. // EventContractPointer
  1521. event_key = "EventContractPointer";
  1522. if(is_calypso_node_present(
  1523. event_bit_representation, event_key, OpusEventStructure)) {
  1524. int positionOffset = get_calypso_node_offset(
  1525. event_bit_representation, event_key, OpusEventStructure);
  1526. int start = positionOffset,
  1527. end = positionOffset +
  1528. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1529. card->opus->events[i - 1].used_contract =
  1530. bit_slice_to_dec(event_bit_representation, start, end);
  1531. if(card->opus->events[i - 1].used_contract > 0) {
  1532. card->events_count++;
  1533. }
  1534. }
  1535. // EventDataSimulation
  1536. event_key = "EventDataSimulation";
  1537. if(is_calypso_node_present(
  1538. event_bit_representation, event_key, OpusEventStructure)) {
  1539. int positionOffset = get_calypso_node_offset(
  1540. event_bit_representation, event_key, OpusEventStructure);
  1541. int start = positionOffset,
  1542. end = positionOffset +
  1543. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1544. card->opus->events[i - 1].simulation =
  1545. bit_slice_to_dec(event_bit_representation, start, end);
  1546. }
  1547. // EventDataRouteDirection
  1548. event_key = "EventDataRouteDirection";
  1549. if(is_calypso_node_present(
  1550. event_bit_representation, event_key, OpusEventStructure)) {
  1551. int positionOffset = get_calypso_node_offset(
  1552. event_bit_representation, event_key, OpusEventStructure);
  1553. int start = positionOffset,
  1554. end = positionOffset +
  1555. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1556. card->opus->events[i - 1].route_direction =
  1557. bit_slice_to_dec(event_bit_representation, start, end);
  1558. }
  1559. // EventDateStamp
  1560. event_key = "EventDateStamp";
  1561. int positionOffset = get_calypso_node_offset(
  1562. event_bit_representation, event_key, OpusEventStructure);
  1563. int start = positionOffset,
  1564. end = positionOffset +
  1565. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1566. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1567. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1568. datetime_timestamp_to_datetime(
  1569. date_timestamp, &card->opus->events[i - 1].date);
  1570. // EventTimeStamp
  1571. event_key = "EventTimeStamp";
  1572. positionOffset = get_calypso_node_offset(
  1573. event_bit_representation, event_key, OpusEventStructure);
  1574. start = positionOffset,
  1575. end = positionOffset +
  1576. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1577. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1578. card->opus->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1579. card->opus->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  1580. card->opus->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  1581. // EventDataDateFirstStamp
  1582. event_key = "EventDataDateFirstStamp";
  1583. positionOffset = get_calypso_node_offset(
  1584. event_bit_representation, event_key, OpusEventStructure);
  1585. start = positionOffset,
  1586. end = positionOffset +
  1587. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1588. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1589. uint64_t first_date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1590. datetime_timestamp_to_datetime(
  1591. first_date_timestamp, &card->opus->events[i - 1].first_stamp_date);
  1592. // EventDataTimeFirstStamp
  1593. event_key = "EventDataTimeFirstStamp";
  1594. positionOffset = get_calypso_node_offset(
  1595. event_bit_representation, event_key, OpusEventStructure);
  1596. start = positionOffset,
  1597. end = positionOffset +
  1598. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1599. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1600. card->opus->events[i - 1].first_stamp_date.hour =
  1601. (decimal_value * 60) / 3600;
  1602. card->opus->events[i - 1].first_stamp_date.minute =
  1603. ((decimal_value * 60) % 3600) / 60;
  1604. card->opus->events[i - 1].first_stamp_date.second =
  1605. ((decimal_value * 60) % 3600) % 60;
  1606. }
  1607. // Free the calypso structure
  1608. free_calypso_structure(OpusEventStructure);
  1609. break;
  1610. }
  1611. case CALYPSO_CARD_UNKNOWN: {
  1612. start = 3;
  1613. end = 6;
  1614. int country_num =
  1615. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1616. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1617. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1618. start = 15;
  1619. end = 18;
  1620. int network_num =
  1621. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1622. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1623. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1624. card->card_type = guess_card_type(country_num, network_num);
  1625. if(card->card_type == CALYPSO_CARD_RAVKAV) {
  1626. card->ravkav = malloc(sizeof(RavKavCardData));
  1627. // Prepare calypso structure
  1628. CalypsoApp* RavKavContractStructure = get_ravkav_contract_structure();
  1629. if(!RavKavContractStructure) {
  1630. FURI_LOG_E(TAG, "Failed to load RavKav Contract structure");
  1631. break;
  1632. }
  1633. //get balance
  1634. error = select_new_app(
  1635. 0x20, 0x2A, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1636. if(error != 0) {
  1637. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1638. break;
  1639. }
  1640. // Check the response after selecting app
  1641. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1642. FURI_LOG_E(
  1643. TAG, "Failed to check response after selecting app for counter");
  1644. break;
  1645. }
  1646. error = read_new_file(
  1647. 1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1648. if(error != 0) {
  1649. FURI_LOG_E(TAG, "Failed to read counter %d", 1);
  1650. break;
  1651. }
  1652. // Check the response after reading the file
  1653. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1654. FURI_LOG_E(
  1655. TAG, "Failed to check response after reading counter %d", 1);
  1656. break;
  1657. }
  1658. uint32_t value = 0;
  1659. for(uint8_t i = 0; i < 3; i++) {
  1660. value = (value << 8) | bit_buffer_get_byte(rx_buffer, i);
  1661. }
  1662. float result = value / 100.0f;
  1663. FURI_LOG_I(TAG, "Value: %.2f ILS", (double)result);
  1664. card->ravkav->contracts[0].balance = result;
  1665. error = select_new_app(
  1666. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1667. if(error != 0) {
  1668. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1669. break;
  1670. }
  1671. // Check the response after selecting app
  1672. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1673. FURI_LOG_E(
  1674. TAG, "Failed to check response after selecting app for contracts");
  1675. break;
  1676. }
  1677. // Now send the read command for contracts
  1678. for(size_t i = 1; i < 2; i++) {
  1679. error = read_new_file(
  1680. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1681. if(error != 0) {
  1682. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1683. break;
  1684. }
  1685. // Check the response after reading the file
  1686. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1687. FURI_LOG_E(
  1688. TAG, "Failed to check response after reading contract %d", i);
  1689. break;
  1690. }
  1691. char bit_representation[response_length * 8 + 1];
  1692. bit_representation[0] = '\0';
  1693. for(size_t i = 0; i < response_length; i++) {
  1694. char bits[9];
  1695. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1696. byte_to_binary(byte, bits);
  1697. strlcat(bit_representation, bits, sizeof(bit_representation));
  1698. }
  1699. bit_representation[response_length * 8] = '\0';
  1700. card->ravkav->contracts[i - 1].present = 1;
  1701. card->events_count = 3;
  1702. card->contracts_count++;
  1703. // ContractVersion
  1704. const char* contract_key = "ContractVersion";
  1705. if(is_calypso_node_present(
  1706. bit_representation, contract_key, RavKavContractStructure)) {
  1707. int positionOffset = get_calypso_node_offset(
  1708. bit_representation, contract_key, RavKavContractStructure);
  1709. int start = positionOffset,
  1710. end = positionOffset +
  1711. get_calypso_node_size(
  1712. contract_key, RavKavContractStructure) -
  1713. 1;
  1714. card->ravkav->contracts[i - 1].version =
  1715. bit_slice_to_dec(bit_representation, start, end);
  1716. }
  1717. // ContractStartDate
  1718. contract_key = "ContractStartDate";
  1719. if(is_calypso_node_present(
  1720. bit_representation, contract_key, RavKavContractStructure)) {
  1721. int positionOffset = get_calypso_node_offset(
  1722. bit_representation, contract_key, RavKavContractStructure);
  1723. int start = positionOffset,
  1724. end = positionOffset +
  1725. get_calypso_node_size(
  1726. contract_key, RavKavContractStructure) -
  1727. 1;
  1728. int decimal_value =
  1729. bit_slice_to_dec(bit_representation, start, end);
  1730. uint32_t invertedDays = decimal_value ^ 0x3FFF;
  1731. int start_validity_timestamp =
  1732. (invertedDays * 3600 * 24) + epoch + 3600;
  1733. datetime_timestamp_to_datetime(
  1734. start_validity_timestamp,
  1735. &card->ravkav->contracts[i - 1].start_date);
  1736. }
  1737. // ContractProvider
  1738. contract_key = "ContractProvider";
  1739. if(is_calypso_node_present(
  1740. bit_representation, contract_key, RavKavContractStructure)) {
  1741. int positionOffset = get_calypso_node_offset(
  1742. bit_representation, contract_key, RavKavContractStructure);
  1743. int start = positionOffset,
  1744. end = positionOffset +
  1745. get_calypso_node_size(
  1746. contract_key, RavKavContractStructure) -
  1747. 1;
  1748. card->ravkav->contracts[i - 1].provider =
  1749. bit_slice_to_dec(bit_representation, start, end);
  1750. FURI_LOG_I(
  1751. TAG,
  1752. "issuer number: %d",
  1753. card->ravkav->contracts[i - 1].provider);
  1754. }
  1755. // ContractTariff
  1756. contract_key = "ContractTariff";
  1757. if(is_calypso_node_present(
  1758. bit_representation, contract_key, RavKavContractStructure)) {
  1759. int positionOffset = get_calypso_node_offset(
  1760. bit_representation, contract_key, RavKavContractStructure);
  1761. int start = positionOffset,
  1762. end = positionOffset +
  1763. get_calypso_node_size(
  1764. contract_key, RavKavContractStructure) -
  1765. 1;
  1766. card->ravkav->contracts[i - 1].tariff =
  1767. bit_slice_to_dec(bit_representation, start, end);
  1768. }
  1769. // ContractSaleDate
  1770. contract_key = "ContractSaleDate";
  1771. int positionOffset = get_calypso_node_offset(
  1772. bit_representation, contract_key, RavKavContractStructure);
  1773. int start = positionOffset,
  1774. end =
  1775. positionOffset +
  1776. get_calypso_node_size(contract_key, RavKavContractStructure) -
  1777. 1;
  1778. uint64_t sale_date_timestamp =
  1779. (bit_slice_to_dec(bit_representation, start, end) * 3600 * 24) +
  1780. (float)epoch + 3600;
  1781. datetime_timestamp_to_datetime(
  1782. sale_date_timestamp, &card->ravkav->contracts[i - 1].sale_date);
  1783. // ContractSaleDevice
  1784. contract_key = "ContractSaleDevice";
  1785. if(is_calypso_node_present(
  1786. bit_representation, contract_key, RavKavContractStructure)) {
  1787. int positionOffset = get_calypso_node_offset(
  1788. bit_representation, contract_key, RavKavContractStructure);
  1789. int start = positionOffset,
  1790. end = positionOffset +
  1791. get_calypso_node_size(
  1792. contract_key, RavKavContractStructure) -
  1793. 1;
  1794. card->ravkav->contracts[i - 1].sale_device =
  1795. bit_slice_to_dec(bit_representation, start, end);
  1796. }
  1797. // ContractSaleNumber
  1798. contract_key = "ContractSaleNumber";
  1799. if(is_calypso_node_present(
  1800. bit_representation, contract_key, RavKavContractStructure)) {
  1801. int positionOffset = get_calypso_node_offset(
  1802. bit_representation, contract_key, RavKavContractStructure);
  1803. int start = positionOffset,
  1804. end = positionOffset +
  1805. get_calypso_node_size(
  1806. contract_key, RavKavContractStructure) -
  1807. 1;
  1808. card->ravkav->contracts[i - 1].sale_number =
  1809. bit_slice_to_dec(bit_representation, start, end);
  1810. }
  1811. // ContractInterchange
  1812. contract_key = "ContractInterchange";
  1813. if(is_calypso_node_present(
  1814. bit_representation, contract_key, RavKavContractStructure)) {
  1815. int positionOffset = get_calypso_node_offset(
  1816. bit_representation, contract_key, RavKavContractStructure);
  1817. int start = positionOffset,
  1818. end = positionOffset +
  1819. get_calypso_node_size(
  1820. contract_key, RavKavContractStructure) -
  1821. 1;
  1822. card->ravkav->contracts[i - 1].interchange =
  1823. bit_slice_to_dec(bit_representation, start, end);
  1824. }
  1825. // ContractInterchange
  1826. contract_key = "ContractRestrictCode";
  1827. if(is_calypso_node_present(
  1828. bit_representation, contract_key, RavKavContractStructure)) {
  1829. int positionOffset = get_calypso_node_offset(
  1830. bit_representation, contract_key, RavKavContractStructure);
  1831. int start = positionOffset,
  1832. end = positionOffset +
  1833. get_calypso_node_size(
  1834. contract_key, RavKavContractStructure) -
  1835. 1;
  1836. card->ravkav->contracts[i - 1].restrict_code_available = true;
  1837. card->ravkav->contracts[i - 1].restrict_code =
  1838. bit_slice_to_dec(bit_representation, start, end);
  1839. }
  1840. // ContractRestrictDuration
  1841. contract_key = "ContractRestrictDuration";
  1842. if(is_calypso_node_present(
  1843. bit_representation, contract_key, RavKavContractStructure)) {
  1844. int positionOffset = get_calypso_node_offset(
  1845. bit_representation, contract_key, RavKavContractStructure);
  1846. int start = positionOffset,
  1847. end = positionOffset +
  1848. get_calypso_node_size(
  1849. contract_key, RavKavContractStructure) -
  1850. 1;
  1851. card->ravkav->contracts[i - 1].restrict_duration_available = true;
  1852. if(card->ravkav->contracts[i - 1].restrict_code == 16) {
  1853. card->ravkav->contracts[i - 1].restrict_duration =
  1854. bit_slice_to_dec(bit_representation, start, end) * 5;
  1855. } else {
  1856. card->ravkav->contracts[i - 1].restrict_duration =
  1857. bit_slice_to_dec(bit_representation, start, end) * 30;
  1858. }
  1859. }
  1860. // ContractEndDate
  1861. contract_key = "ContractEndDate";
  1862. if(is_calypso_node_present(
  1863. bit_representation, contract_key, RavKavContractStructure)) {
  1864. int positionOffset = get_calypso_node_offset(
  1865. bit_representation, contract_key, RavKavContractStructure);
  1866. int start = positionOffset,
  1867. end = positionOffset +
  1868. get_calypso_node_size(
  1869. contract_key, RavKavContractStructure) -
  1870. 1;
  1871. card->ravkav->contracts[i - 1].end_date_available = true;
  1872. int end_date_timestamp =
  1873. (bit_slice_to_dec(bit_representation, start, end) * 3600 *
  1874. 24) +
  1875. epoch + 3600;
  1876. datetime_timestamp_to_datetime(
  1877. end_date_timestamp, &card->ravkav->contracts[i - 1].end_date);
  1878. }
  1879. }
  1880. // Free the calypso structure
  1881. free_calypso_structure(RavKavContractStructure);
  1882. error = select_new_app(
  1883. 0x20, 0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1884. if(error != 0) {
  1885. FURI_LOG_E(TAG, "Failed to select app for environment");
  1886. break;
  1887. }
  1888. // Check the response after selecting app
  1889. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1890. FURI_LOG_E(
  1891. TAG,
  1892. "Failed to check response after selecting app for environment");
  1893. break;
  1894. }
  1895. // Prepare calypso structure
  1896. CalypsoApp* RavKavEnvStructure = get_ravkav_env_holder_structure();
  1897. if(!RavKavEnvStructure) {
  1898. FURI_LOG_E(TAG, "Failed to load RavKav environment structure");
  1899. break;
  1900. }
  1901. // Now send the read command for environment
  1902. error = read_new_file(
  1903. 1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1904. if(error != 0) {
  1905. FURI_LOG_E(TAG, "Failed to read environment");
  1906. break;
  1907. }
  1908. // Check the response after reading the file
  1909. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1910. FURI_LOG_E(TAG, "Failed to check response after reading environment");
  1911. break;
  1912. }
  1913. char env_bit_representation[response_length * 8 + 1];
  1914. env_bit_representation[0] = '\0';
  1915. for(size_t i = 0; i < response_length; i++) {
  1916. char bits[9];
  1917. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1918. byte_to_binary(byte, bits);
  1919. strlcat(env_bit_representation, bits, sizeof(env_bit_representation));
  1920. }
  1921. env_bit_representation[response_length * 8] = '\0';
  1922. // EnvApplicationVersionNumber
  1923. char* env_key = "EnvApplicationVersionNumber";
  1924. if(is_calypso_node_present(
  1925. env_bit_representation, env_key, RavKavEnvStructure)) {
  1926. int positionOffset = get_calypso_node_offset(
  1927. env_bit_representation, env_key, RavKavEnvStructure);
  1928. int start = positionOffset,
  1929. end = positionOffset +
  1930. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1931. card->ravkav->environment.app_num =
  1932. bit_slice_to_dec(env_bit_representation, start, end);
  1933. }
  1934. // EnvApplicationNumber
  1935. env_key = "EnvApplicationNumber";
  1936. if(is_calypso_node_present(
  1937. env_bit_representation, env_key, RavKavEnvStructure)) {
  1938. int positionOffset = get_calypso_node_offset(
  1939. env_bit_representation, env_key, RavKavEnvStructure);
  1940. int start = positionOffset,
  1941. end = positionOffset +
  1942. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1943. card->ravkav->environment.app_num =
  1944. bit_slice_to_dec(env_bit_representation, start, end);
  1945. }
  1946. // EnvDateOfIssue
  1947. env_key = "EnvDateOfIssue";
  1948. if(is_calypso_node_present(
  1949. env_bit_representation, env_key, RavKavEnvStructure)) {
  1950. int positionOffset = get_calypso_node_offset(
  1951. env_bit_representation, env_key, RavKavEnvStructure);
  1952. int start = positionOffset,
  1953. end = positionOffset +
  1954. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1955. uint64_t issue_date_timestamp =
  1956. (bit_slice_to_dec(env_bit_representation, start, end) * 3600 *
  1957. 24) +
  1958. (float)epoch + 3600;
  1959. datetime_timestamp_to_datetime(
  1960. issue_date_timestamp, &card->ravkav->environment.issue_dt);
  1961. }
  1962. // EnvEndValidity
  1963. env_key = "EnvEndValidity";
  1964. if(is_calypso_node_present(
  1965. env_bit_representation, env_key, RavKavEnvStructure)) {
  1966. int positionOffset = get_calypso_node_offset(
  1967. env_bit_representation, env_key, RavKavEnvStructure);
  1968. int start = positionOffset,
  1969. end = positionOffset +
  1970. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1971. uint64_t end_date_timestamp =
  1972. (bit_slice_to_dec(env_bit_representation, start, end) * 3600 *
  1973. 24) +
  1974. (float)epoch + 3600;
  1975. datetime_timestamp_to_datetime(
  1976. end_date_timestamp, &card->ravkav->environment.end_dt);
  1977. }
  1978. // EnvPayMethod
  1979. env_key = "EnvPayMethod";
  1980. if(is_calypso_node_present(
  1981. env_bit_representation, env_key, RavKavEnvStructure)) {
  1982. int positionOffset = get_calypso_node_offset(
  1983. env_bit_representation, env_key, RavKavEnvStructure);
  1984. int start = positionOffset,
  1985. end = positionOffset +
  1986. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1987. card->ravkav->environment.pay_method =
  1988. bit_slice_to_dec(env_bit_representation, start, end);
  1989. }
  1990. free_calypso_structure(RavKavEnvStructure);
  1991. // Select app for events
  1992. error = select_new_app(
  1993. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1994. if(error != 0) {
  1995. break;
  1996. }
  1997. // Check the response after selecting app
  1998. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1999. break;
  2000. }
  2001. // Load the calypso structure for events
  2002. CalypsoApp* RavKavEventStructure = get_ravkav_event_structure();
  2003. if(!RavKavEventStructure) {
  2004. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  2005. break;
  2006. }
  2007. // Now send the read command for events
  2008. for(size_t i = 1; i < 4; i++) {
  2009. error = read_new_file(
  2010. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  2011. if(error != 0) {
  2012. break;
  2013. }
  2014. // Check the response after reading the file
  2015. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  2016. break;
  2017. }
  2018. char event_bit_representation[response_length * 8 + 1];
  2019. event_bit_representation[0] = '\0';
  2020. for(size_t i = 0; i < response_length; i++) {
  2021. char bits[9];
  2022. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  2023. byte_to_binary(byte, bits);
  2024. strlcat(
  2025. event_bit_representation,
  2026. bits,
  2027. sizeof(event_bit_representation));
  2028. }
  2029. FURI_LOG_I(TAG, "event bit repr %s", event_bit_representation);
  2030. // EventVersion
  2031. const char* event_key = "EventVersion";
  2032. if(is_calypso_node_present(
  2033. event_bit_representation, event_key, RavKavEventStructure)) {
  2034. int positionOffset = get_calypso_node_offset(
  2035. event_bit_representation, event_key, RavKavEventStructure);
  2036. int start = positionOffset,
  2037. end = positionOffset +
  2038. get_calypso_node_size(event_key, RavKavEventStructure) -
  2039. 1;
  2040. card->ravkav->events[i - 1].event_version =
  2041. bit_slice_to_dec(event_bit_representation, start, end);
  2042. }
  2043. // EventServiceProvider
  2044. event_key = "EventServiceProvider";
  2045. if(is_calypso_node_present(
  2046. event_bit_representation, event_key, RavKavEventStructure)) {
  2047. int positionOffset = get_calypso_node_offset(
  2048. event_bit_representation, event_key, RavKavEventStructure);
  2049. int start = positionOffset,
  2050. end = positionOffset +
  2051. get_calypso_node_size(event_key, RavKavEventStructure) -
  2052. 1;
  2053. FURI_LOG_I(TAG, "service provider: start: %d, end %d", start, end);
  2054. card->ravkav->events[i - 1].service_provider =
  2055. bit_slice_to_dec(event_bit_representation, start, end);
  2056. }
  2057. // EventContractID
  2058. event_key = "EventContractID";
  2059. if(is_calypso_node_present(
  2060. event_bit_representation, event_key, RavKavEventStructure)) {
  2061. int positionOffset = get_calypso_node_offset(
  2062. event_bit_representation, event_key, RavKavEventStructure);
  2063. int start = positionOffset,
  2064. end = positionOffset +
  2065. get_calypso_node_size(event_key, RavKavEventStructure) -
  2066. 1;
  2067. card->ravkav->events[i - 1].contract_id =
  2068. bit_slice_to_dec(event_bit_representation, start, end);
  2069. FURI_LOG_I(TAG, "2: start: %d, end %d", start, end);
  2070. }
  2071. // EventAreaID
  2072. event_key = "EventAreaID";
  2073. if(is_calypso_node_present(
  2074. event_bit_representation, event_key, RavKavEventStructure)) {
  2075. int positionOffset = get_calypso_node_offset(
  2076. event_bit_representation, event_key, RavKavEventStructure);
  2077. int start = positionOffset,
  2078. end = positionOffset +
  2079. get_calypso_node_size(event_key, RavKavEventStructure) -
  2080. 1;
  2081. card->ravkav->events[i - 1].area_id =
  2082. bit_slice_to_dec(event_bit_representation, start, end);
  2083. FURI_LOG_I(TAG, "3: start: %d, end %d", start, end);
  2084. }
  2085. // EventType
  2086. event_key = "EventType";
  2087. if(is_calypso_node_present(
  2088. event_bit_representation, event_key, RavKavEventStructure)) {
  2089. int positionOffset = get_calypso_node_offset(
  2090. event_bit_representation, event_key, RavKavEventStructure);
  2091. int start = positionOffset,
  2092. end = positionOffset +
  2093. get_calypso_node_size(event_key, RavKavEventStructure) -
  2094. 1;
  2095. card->ravkav->events[i - 1].type =
  2096. bit_slice_to_dec(event_bit_representation, start, end);
  2097. FURI_LOG_I(TAG, "4: start: %d, end %d", start, end);
  2098. }
  2099. // EventRouteNumber
  2100. event_key = "EventExtension";
  2101. if(is_calypso_node_present(
  2102. event_bit_representation, event_key, RavKavEventStructure)) {
  2103. int positionOffset = get_calypso_node_offset(
  2104. event_bit_representation, event_key, RavKavEventStructure);
  2105. int start = positionOffset,
  2106. end = positionOffset +
  2107. get_calypso_node_size(event_key, RavKavEventStructure) -
  2108. 1;
  2109. FURI_LOG_I(TAG, "event extension : start: %d, end %d", start, end);
  2110. FURI_LOG_I(
  2111. TAG,
  2112. "event extension bitmap: %d",
  2113. bit_slice_to_dec(event_bit_representation, start, end));
  2114. }
  2115. // EventTime
  2116. event_key = "EventTime";
  2117. if(is_calypso_node_present(
  2118. event_bit_representation, event_key, RavKavEventStructure)) {
  2119. int positionOffset = get_calypso_node_offset(
  2120. event_bit_representation, event_key, RavKavEventStructure);
  2121. int start = positionOffset,
  2122. end = positionOffset +
  2123. get_calypso_node_size(event_key, RavKavEventStructure) -
  2124. 1;
  2125. uint64_t event_timestamp =
  2126. bit_slice_to_dec(event_bit_representation, start, end) +
  2127. (float)epoch + 3600;
  2128. datetime_timestamp_to_datetime(
  2129. event_timestamp, &card->ravkav->events[i - 1].time);
  2130. FURI_LOG_I(TAG, "5: start: %d, end %d", start, end);
  2131. }
  2132. // EventInterchangeFlag
  2133. event_key = "EventInterchangeFlag";
  2134. if(is_calypso_node_present(
  2135. event_bit_representation, event_key, RavKavEventStructure)) {
  2136. int positionOffset = get_calypso_node_offset(
  2137. event_bit_representation, event_key, RavKavEventStructure);
  2138. int start = positionOffset,
  2139. end = positionOffset +
  2140. get_calypso_node_size(event_key, RavKavEventStructure) -
  2141. 1;
  2142. card->ravkav->events[i - 1].interchange_flag =
  2143. bit_slice_to_dec(event_bit_representation, start, end);
  2144. FURI_LOG_I(TAG, "6: start: %d, end %d", start, end);
  2145. }
  2146. // EventRouteNumber
  2147. event_key = "EventRouteNumber";
  2148. if(is_calypso_node_present(
  2149. event_bit_representation, event_key, RavKavEventStructure)) {
  2150. int positionOffset = get_calypso_node_offset(
  2151. event_bit_representation, event_key, RavKavEventStructure);
  2152. int start = positionOffset,
  2153. end = positionOffset +
  2154. get_calypso_node_size(event_key, RavKavEventStructure) -
  2155. 1;
  2156. card->ravkav->events[i - 1].route_number =
  2157. bit_slice_to_dec(event_bit_representation, start, end);
  2158. card->ravkav->events[i - 1].route_number_available = true;
  2159. FURI_LOG_I(TAG, "7: start: %d, end %d", start, end);
  2160. }
  2161. // EventRouteNumber
  2162. event_key = "EventfareCode";
  2163. if(is_calypso_node_present(
  2164. event_bit_representation, event_key, RavKavEventStructure)) {
  2165. int positionOffset = get_calypso_node_offset(
  2166. event_bit_representation, event_key, RavKavEventStructure);
  2167. int start = positionOffset,
  2168. end = positionOffset +
  2169. get_calypso_node_size(event_key, RavKavEventStructure) -
  2170. 1;
  2171. card->ravkav->events[i - 1].fare_code =
  2172. bit_slice_to_dec(event_bit_representation, start, end);
  2173. card->ravkav->events[i - 1].fare_code = true;
  2174. FURI_LOG_I(TAG, "8: start: %d, end %d", start, end);
  2175. }
  2176. // EventRouteNumber
  2177. event_key = "EventDebitAmount";
  2178. if(is_calypso_node_present(
  2179. event_bit_representation, event_key, RavKavEventStructure)) {
  2180. int positionOffset = get_calypso_node_offset(
  2181. event_bit_representation, event_key, RavKavEventStructure);
  2182. int start = positionOffset,
  2183. end = positionOffset +
  2184. get_calypso_node_size(event_key, RavKavEventStructure) -
  2185. 1;
  2186. card->ravkav->events[i - 1].debit_amount =
  2187. bit_slice_to_dec(event_bit_representation, start, end) / 100.0;
  2188. card->ravkav->events[i - 1].debit_amount_available = true;
  2189. FURI_LOG_I(TAG, "9: start: %d, end %d", start, end);
  2190. }
  2191. // EventRouteNumber
  2192. event_key = "Location";
  2193. if(is_calypso_node_present(
  2194. event_bit_representation, event_key, RavKavEventStructure)) {
  2195. int positionOffset = get_calypso_node_offset(
  2196. event_bit_representation, event_key, RavKavEventStructure);
  2197. int start = positionOffset,
  2198. end = positionOffset +
  2199. get_calypso_node_size(event_key, RavKavEventStructure) -
  2200. 1;
  2201. FURI_LOG_I(TAG, "location : start: %d, end %d", start, end);
  2202. FURI_LOG_I(
  2203. TAG,
  2204. "locatrion bitmap: %d",
  2205. bit_slice_to_dec(event_bit_representation, start, end));
  2206. }
  2207. }
  2208. // Free the calypso structure
  2209. free_calypso_structure(RavKavEventStructure);
  2210. break;
  2211. }
  2212. }
  2213. default:
  2214. break;
  2215. }
  2216. widget_add_text_scroll_element(
  2217. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  2218. CalypsoContext* context = malloc(sizeof(CalypsoContext));
  2219. context->card = card;
  2220. context->page_id = 0;
  2221. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  2222. app->calypso_context = context;
  2223. // Ensure no nested mutexes
  2224. furi_mutex_acquire(context->mutex, FuriWaitForever);
  2225. update_page_info(app, parsed_data);
  2226. furi_mutex_release(context->mutex);
  2227. widget_add_text_scroll_element(
  2228. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  2229. // Ensure no nested mutexes
  2230. furi_mutex_acquire(context->mutex, FuriWaitForever);
  2231. update_widget_elements(app);
  2232. furi_mutex_release(context->mutex);
  2233. furi_string_free(parsed_data);
  2234. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  2235. metroflip_app_blink_stop(app);
  2236. stage = MetroflipPollerEventTypeSuccess;
  2237. next_command = NfcCommandStop;
  2238. } while(false);
  2239. if(stage != MetroflipPollerEventTypeSuccess) {
  2240. next_command = NfcCommandStop;
  2241. }
  2242. }
  2243. }
  2244. bit_buffer_free(tx_buffer);
  2245. bit_buffer_free(rx_buffer);
  2246. return next_command;
  2247. }
  2248. static void calypso_on_enter(Metroflip* app) {
  2249. dolphin_deed(DolphinDeedNfcRead);
  2250. // Setup view
  2251. Popup* popup = app->popup;
  2252. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  2253. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  2254. // Start worker
  2255. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  2256. nfc_scanner_alloc(app->nfc);
  2257. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  2258. nfc_poller_start(app->poller, calypso_poller_callback, app);
  2259. metroflip_app_blink_start(app);
  2260. }
  2261. static bool calypso_on_event(Metroflip* app, SceneManagerEvent event) {
  2262. bool consumed = false;
  2263. if(event.type == SceneManagerEventTypeCustom) {
  2264. if(event.event == MetroflipPollerEventTypeCardDetect) {
  2265. Popup* popup = app->popup;
  2266. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  2267. consumed = true;
  2268. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  2269. Popup* popup = app->popup;
  2270. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  2271. consumed = true;
  2272. } else if(event.event == MetroflipCustomEventPollerFail) {
  2273. Popup* popup = app->popup;
  2274. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  2275. consumed = true;
  2276. }
  2277. } else if(event.type == SceneManagerEventTypeBack) {
  2278. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  2279. scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
  2280. consumed = true;
  2281. }
  2282. return consumed;
  2283. }
  2284. static void calypso_on_exit(Metroflip* app) {
  2285. if(app->poller && !app->data_loaded) {
  2286. nfc_poller_stop(app->poller);
  2287. nfc_poller_free(app->poller);
  2288. }
  2289. metroflip_app_blink_stop(app);
  2290. widget_reset(app->widget);
  2291. // Clear view
  2292. popup_reset(app->popup);
  2293. if(app->calypso_context) {
  2294. CalypsoContext* ctx = app->calypso_context;
  2295. free(ctx->card->navigo);
  2296. free(ctx->card->opus);
  2297. free(ctx->card);
  2298. furi_mutex_free(ctx->mutex);
  2299. free(ctx);
  2300. app->calypso_context = NULL;
  2301. }
  2302. }
  2303. /* Actual implementation of app<>plugin interface */
  2304. static const MetroflipPlugin calypso_plugin = {
  2305. .card_name = "Calypso",
  2306. .plugin_on_enter = calypso_on_enter,
  2307. .plugin_on_event = calypso_on_event,
  2308. .plugin_on_exit = calypso_on_exit,
  2309. };
  2310. /* Plugin descriptor to comply with basic plugin specification */
  2311. static const FlipperAppPluginDescriptor calypso_plugin_descriptor = {
  2312. .appid = METROFLIP_SUPPORTED_CARD_PLUGIN_APP_ID,
  2313. .ep_api_version = METROFLIP_SUPPORTED_CARD_PLUGIN_API_VERSION,
  2314. .entry_point = &calypso_plugin,
  2315. };
  2316. /* Plugin entry point - must return a pointer to const descriptor */
  2317. const FlipperAppPluginDescriptor* calypso_plugin_ep(void) {
  2318. return &calypso_plugin_descriptor;
  2319. }