passy_reader.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. #include "passy_reader.h"
  2. #define TAG "PassyReader"
  3. #define PASSY_READER_DG1_CHUNK_SIZE 0x20
  4. #define PASSY_READER_DG2_CHUNK_SIZE 0x20
  5. #define ASN_EMIT_DEBUG 0
  6. #include <lib/asn1/COM.h>
  7. static uint8_t passport_aid[] = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01};
  8. static uint8_t select_header[] = {0x00, 0xA4, 0x04, 0x0C};
  9. static uint8_t get_challenge[] = {0x00, 0x84, 0x00, 0x00, 0x08};
  10. static uint8_t SW_success[] = {0x90, 0x00};
  11. static const uint8_t jpeg_header[4] = {0xFF, 0xD8, 0xFF, 0xE0};
  12. static const uint8_t jpeg2k_header[6] = {0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50};
  13. static const uint8_t jpeg2k_cs_header[4] = {0xFF, 0x4F, 0xFF, 0x51};
  14. size_t asn1_length(uint8_t data[3]) {
  15. if(data[0] <= 0x7F) {
  16. return data[0];
  17. } else if(data[0] == 0x81) {
  18. return data[1];
  19. } else if(data[0] == 0x82) {
  20. return (data[1] << 8) | data[2];
  21. }
  22. return 0;
  23. }
  24. size_t asn1_length_length(uint8_t data[3]) {
  25. if(data[0] <= 0x7F) {
  26. return 1;
  27. } else if(data[0] == 0x81) {
  28. return 2;
  29. } else if(data[0] == 0x82) {
  30. return 3;
  31. }
  32. return 0;
  33. }
  34. PassyReader* passy_reader_alloc(Passy* passy) {
  35. PassyReader* passy_reader = malloc(sizeof(PassyReader));
  36. memset(passy_reader, 0, sizeof(PassyReader));
  37. furi_assert(passy);
  38. passy_reader->passy = passy;
  39. passy_reader->DG1 = passy->DG1;
  40. passy_reader->tx_buffer = bit_buffer_alloc(PASSY_READER_MAX_BUFFER_SIZE);
  41. passy_reader->rx_buffer = bit_buffer_alloc(PASSY_READER_MAX_BUFFER_SIZE);
  42. char passport_number[11];
  43. memset(passport_number, 0, sizeof(passport_number));
  44. memcpy(passport_number, passy->passport_number, strlen(passy->passport_number));
  45. passport_number[strlen(passy->passport_number)] = passy_checksum(passy->passport_number);
  46. FURI_LOG_I(TAG, "Passport number: %s", passport_number);
  47. char date_of_birth[8];
  48. memset(date_of_birth, 0, sizeof(date_of_birth));
  49. memcpy(date_of_birth, passy->date_of_birth, strlen(passy->date_of_birth));
  50. date_of_birth[strlen(passy->date_of_birth)] = passy_checksum(passy->date_of_birth);
  51. FURI_LOG_I(TAG, "Date of birth: %s", date_of_birth);
  52. char date_of_expiry[8];
  53. memset(date_of_expiry, 0, sizeof(date_of_expiry));
  54. memcpy(date_of_expiry, passy->date_of_expiry, strlen(passy->date_of_expiry));
  55. date_of_expiry[strlen(passy->date_of_expiry)] = passy_checksum(passy->date_of_expiry);
  56. FURI_LOG_I(TAG, "Date of expiry: %s", date_of_expiry);
  57. passy_reader->secure_messaging = secure_messaging_alloc(
  58. (uint8_t*)passport_number, (uint8_t*)date_of_birth, (uint8_t*)date_of_expiry);
  59. return passy_reader;
  60. }
  61. void passy_reader_free(PassyReader* passy_reader) {
  62. furi_assert(passy_reader);
  63. bit_buffer_free(passy_reader->tx_buffer);
  64. bit_buffer_free(passy_reader->rx_buffer);
  65. if(passy_reader->secure_messaging) {
  66. secure_messaging_free(passy_reader->secure_messaging);
  67. }
  68. free(passy_reader);
  69. }
  70. NfcCommand passy_reader_send(PassyReader* passy_reader) {
  71. NfcCommand ret = NfcCommandContinue;
  72. BitBuffer* tx_buffer = passy_reader->tx_buffer;
  73. BitBuffer* rx_buffer = passy_reader->rx_buffer;
  74. Iso14443_4bPoller* iso14443_4b_poller = passy_reader->iso14443_4b_poller;
  75. Iso14443_4bError error;
  76. passy_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
  77. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  78. if(error != Iso14443_4bErrorNone) {
  79. FURI_LOG_W(TAG, "iso14443_4b_poller_send_block error %d", error);
  80. return NfcCommandStop;
  81. }
  82. bit_buffer_reset(tx_buffer);
  83. passy_log_bitbuffer(TAG, "NFC response", rx_buffer);
  84. // Check SW
  85. size_t length = bit_buffer_get_size_bytes(rx_buffer);
  86. const uint8_t* data = bit_buffer_get_data(rx_buffer);
  87. if(length < 2) {
  88. FURI_LOG_W(TAG, "Invalid response length %d", length);
  89. return NfcCommandStop;
  90. }
  91. passy_reader->last_sw = (data[length - 2] << 8) | data[length - 1];
  92. if(memcmp(data + length - 2, SW_success, sizeof(SW_success)) != 0) {
  93. FURI_LOG_W(TAG, "Invalid SW %02x %02x", data[length - 2], data[length - 1]);
  94. return NfcCommandStop;
  95. }
  96. return ret;
  97. }
  98. NfcCommand passy_reader_select_application(PassyReader* passy_reader) {
  99. NfcCommand ret = NfcCommandContinue;
  100. BitBuffer* tx_buffer = passy_reader->tx_buffer;
  101. bit_buffer_append_bytes(tx_buffer, select_header, sizeof(select_header));
  102. bit_buffer_append_byte(tx_buffer, sizeof(passport_aid));
  103. bit_buffer_append_bytes(tx_buffer, passport_aid, sizeof(passport_aid));
  104. bit_buffer_append_byte(tx_buffer, 0x00); // Le
  105. ret = passy_reader_send(passy_reader);
  106. if(ret != NfcCommandContinue) {
  107. return ret;
  108. }
  109. return ret;
  110. }
  111. NfcCommand passy_reader_get_challenge(PassyReader* passy_reader) {
  112. NfcCommand ret = NfcCommandContinue;
  113. bit_buffer_append_bytes(passy_reader->tx_buffer, get_challenge, sizeof(get_challenge));
  114. ret = passy_reader_send(passy_reader);
  115. if(ret != NfcCommandContinue) {
  116. return ret;
  117. }
  118. const uint8_t* data = bit_buffer_get_data(passy_reader->rx_buffer);
  119. SecureMessaging* secure_messaging = passy_reader->secure_messaging;
  120. const uint8_t* rnd_icc = data;
  121. memcpy(secure_messaging->rndICC, rnd_icc, 8);
  122. return ret;
  123. }
  124. NfcCommand passy_reader_authenticate(PassyReader* passy_reader) {
  125. NfcCommand ret = NfcCommandContinue;
  126. BitBuffer* tx_buffer = passy_reader->tx_buffer;
  127. // TODO: move into secure_messaging
  128. SecureMessaging* secure_messaging = passy_reader->secure_messaging;
  129. uint8_t S[32];
  130. memset(S, 0, sizeof(S));
  131. uint8_t eifd[32];
  132. memcpy(S, secure_messaging->rndIFD, sizeof(secure_messaging->rndIFD));
  133. memcpy(
  134. S + sizeof(secure_messaging->rndIFD),
  135. secure_messaging->rndICC,
  136. sizeof(secure_messaging->rndICC));
  137. memcpy(
  138. S + sizeof(secure_messaging->rndIFD) + sizeof(secure_messaging->rndICC),
  139. secure_messaging->Kifd,
  140. sizeof(secure_messaging->Kifd));
  141. uint8_t iv[8];
  142. memset(iv, 0, sizeof(iv));
  143. mbedtls_des3_context ctx;
  144. mbedtls_des3_init(&ctx);
  145. mbedtls_des3_set2key_enc(&ctx, secure_messaging->KENC);
  146. mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, sizeof(S), iv, S, eifd);
  147. mbedtls_des3_free(&ctx);
  148. passy_log_buffer(TAG, "S", S, sizeof(S));
  149. passy_log_buffer(TAG, "eifd", eifd, sizeof(eifd));
  150. uint8_t mifd[8];
  151. passy_mac(secure_messaging->KMAC, eifd, sizeof(eifd), mifd, false);
  152. passy_log_buffer(TAG, "mifd", mifd, sizeof(mifd));
  153. uint8_t authenticate_header[] = {0x00, 0x82, 0x00, 0x00};
  154. bit_buffer_append_bytes(tx_buffer, authenticate_header, sizeof(authenticate_header));
  155. bit_buffer_append_byte(tx_buffer, sizeof(eifd) + sizeof(mifd));
  156. bit_buffer_append_bytes(tx_buffer, eifd, sizeof(eifd));
  157. bit_buffer_append_bytes(tx_buffer, mifd, sizeof(mifd));
  158. bit_buffer_append_byte(tx_buffer, 0x28); // Le
  159. ret = passy_reader_send(passy_reader);
  160. if(ret != NfcCommandContinue) {
  161. return ret;
  162. }
  163. const uint8_t* data = bit_buffer_get_data(passy_reader->rx_buffer);
  164. size_t length = bit_buffer_get_size_bytes(passy_reader->rx_buffer);
  165. const uint8_t* mac = data + length - 2 - 8;
  166. uint8_t calculated_mac[8];
  167. passy_mac(secure_messaging->KMAC, (uint8_t*)data, length - 8 - 2, calculated_mac, false);
  168. if(memcmp(mac, calculated_mac, sizeof(calculated_mac)) != 0) {
  169. FURI_LOG_W(TAG, "Invalid MAC");
  170. return NfcCommandStop;
  171. }
  172. uint8_t decrypted[32];
  173. do {
  174. uint8_t iv[8];
  175. memset(iv, 0, sizeof(iv));
  176. mbedtls_des3_context ctx;
  177. mbedtls_des3_init(&ctx);
  178. mbedtls_des3_set2key_dec(&ctx, secure_messaging->KENC);
  179. mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length - 2 - 8, iv, data, decrypted);
  180. mbedtls_des3_free(&ctx);
  181. } while(false);
  182. passy_log_buffer(TAG, "decrypted", decrypted, sizeof(decrypted));
  183. uint8_t* rnd_icc = decrypted;
  184. uint8_t* rnd_ifd = decrypted + 8;
  185. uint8_t* Kicc = decrypted + 16;
  186. if(memcmp(rnd_icc, secure_messaging->rndICC, sizeof(secure_messaging->rndICC)) != 0) {
  187. FURI_LOG_W(TAG, "Invalid rndICC");
  188. return NfcCommandStop;
  189. }
  190. memcpy(secure_messaging->Kicc, Kicc, sizeof(secure_messaging->Kicc));
  191. memcpy(secure_messaging->SSC + 0, rnd_icc + 4, 4);
  192. memcpy(secure_messaging->SSC + 4, rnd_ifd + 4, 4);
  193. return ret;
  194. }
  195. NfcCommand passy_reader_select_file(PassyReader* passy_reader, uint16_t file_id) {
  196. NfcCommand ret = NfcCommandContinue;
  197. uint8_t select_0101[] = {0x00, 0xa4, 0x02, 0x0c, 0x02, 0x00, 0x00};
  198. select_0101[5] = (file_id >> 8) & 0xFF;
  199. select_0101[6] = file_id & 0xFF;
  200. secure_messaging_wrap_apdu(
  201. passy_reader->secure_messaging, select_0101, sizeof(select_0101), passy_reader->tx_buffer);
  202. ret = passy_reader_send(passy_reader);
  203. if(ret != NfcCommandContinue) {
  204. return ret;
  205. }
  206. secure_messaging_unwrap_rapdu(passy_reader->secure_messaging, passy_reader->rx_buffer);
  207. passy_log_bitbuffer(TAG, "NFC response (decrypted)", passy_reader->rx_buffer);
  208. return ret;
  209. }
  210. NfcCommand passy_reader_read_binary(
  211. PassyReader* passy_reader,
  212. uint16_t offset,
  213. uint8_t Le,
  214. uint8_t* output_buffer) {
  215. NfcCommand ret = NfcCommandContinue;
  216. if(offset & 0x8000) {
  217. FURI_LOG_W(TAG, "Invalid offset %04x", offset);
  218. }
  219. uint8_t read_binary[] = {0x00, 0xB0, (offset >> 8) & 0xff, (offset >> 0) & 0xff, Le};
  220. secure_messaging_wrap_apdu(
  221. passy_reader->secure_messaging, read_binary, sizeof(read_binary), passy_reader->tx_buffer);
  222. ret = passy_reader_send(passy_reader);
  223. if(ret != NfcCommandContinue) {
  224. return ret;
  225. }
  226. secure_messaging_unwrap_rapdu(passy_reader->secure_messaging, passy_reader->rx_buffer);
  227. const uint8_t* decrypted_data = bit_buffer_get_data(passy_reader->rx_buffer);
  228. memcpy(output_buffer, decrypted_data, Le);
  229. return ret;
  230. }
  231. NfcCommand passy_reader_state_machine(PassyReader* passy_reader) {
  232. furi_assert(passy_reader);
  233. Passy* passy = passy_reader->passy;
  234. NfcCommand ret = NfcCommandContinue;
  235. do {
  236. ret = passy_reader_select_application(passy_reader);
  237. if(ret != NfcCommandContinue) {
  238. view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
  239. break;
  240. }
  241. ret = passy_reader_get_challenge(passy_reader);
  242. if(ret != NfcCommandContinue) {
  243. view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
  244. break;
  245. }
  246. ret = passy_reader_authenticate(passy_reader);
  247. if(ret != NfcCommandContinue) {
  248. view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
  249. break;
  250. }
  251. FURI_LOG_I(TAG, "Mututal authentication success");
  252. secure_messaging_calculate_session_keys(passy_reader->secure_messaging);
  253. view_dispatcher_send_custom_event(
  254. passy->view_dispatcher, PassyCustomEventReaderAuthenticated);
  255. ret = passy_reader_select_file(passy_reader, passy->read_type);
  256. if(ret != NfcCommandContinue) {
  257. view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
  258. break;
  259. }
  260. if(passy->read_type == PassyReadCOM) {
  261. uint8_t header[4];
  262. ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
  263. if(ret != NfcCommandContinue) {
  264. view_dispatcher_send_custom_event(
  265. passy->view_dispatcher, PassyCustomEventReaderError);
  266. break;
  267. }
  268. size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
  269. uint8_t body_offset = sizeof(header);
  270. BitBuffer* com_buffer = bit_buffer_alloc(body_size);
  271. bit_buffer_append_bytes(com_buffer, header, sizeof(header));
  272. do {
  273. view_dispatcher_send_custom_event(
  274. passy->view_dispatcher, PassyCustomEventReaderReading);
  275. uint8_t chunk[PASSY_READER_DG1_CHUNK_SIZE];
  276. uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - body_offset));
  277. ret = passy_reader_read_binary(passy_reader, body_offset, Le, chunk);
  278. if(ret != NfcCommandContinue) {
  279. view_dispatcher_send_custom_event(
  280. passy->view_dispatcher, PassyCustomEventReaderError);
  281. break;
  282. }
  283. bit_buffer_append_bytes(com_buffer, chunk, Le);
  284. body_offset += Le;
  285. } while(body_offset < body_size);
  286. COM_t* com = 0;
  287. com = calloc(1, sizeof *com);
  288. assert(com);
  289. asn_dec_rval_t rval = asn_decode(
  290. 0,
  291. ATS_DER,
  292. &asn_DEF_COM,
  293. (void**)&com,
  294. bit_buffer_get_data(com_buffer),
  295. bit_buffer_get_size_bytes(com_buffer));
  296. if(rval.code == RC_OK) {
  297. FURI_LOG_I(TAG, "ASN.1 decode success");
  298. char payloadDebug[384] = {0};
  299. memset(payloadDebug, 0, sizeof(payloadDebug));
  300. (&asn_DEF_COM)
  301. ->op->print_struct(&asn_DEF_COM, com, 1, print_struct_callback, payloadDebug);
  302. if(strlen(payloadDebug) > 0) {
  303. FURI_LOG_D(TAG, "COM: %s", payloadDebug);
  304. } else {
  305. FURI_LOG_D(TAG, "Received empty Payload");
  306. }
  307. } else {
  308. FURI_LOG_E(TAG, "ASN.1 decode failed: %d. %d consumed", rval.code, rval.consumed);
  309. passy_log_bitbuffer(TAG, "COM", com_buffer);
  310. }
  311. bit_buffer_free(com_buffer);
  312. free(com);
  313. com = 0;
  314. } else if(passy->read_type == PassyReadDG1) {
  315. bit_buffer_reset(passy->DG1);
  316. uint8_t header[4];
  317. ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
  318. if(ret != NfcCommandContinue) {
  319. view_dispatcher_send_custom_event(
  320. passy->view_dispatcher, PassyCustomEventReaderError);
  321. break;
  322. }
  323. size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
  324. uint8_t body_offset = sizeof(header);
  325. bit_buffer_append_bytes(passy_reader->DG1, header, sizeof(header));
  326. do {
  327. view_dispatcher_send_custom_event(
  328. passy->view_dispatcher, PassyCustomEventReaderReading);
  329. uint8_t chunk[PASSY_READER_DG1_CHUNK_SIZE];
  330. uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - body_offset));
  331. ret = passy_reader_read_binary(passy_reader, body_offset, Le, chunk);
  332. if(ret != NfcCommandContinue) {
  333. view_dispatcher_send_custom_event(
  334. passy->view_dispatcher, PassyCustomEventReaderError);
  335. break;
  336. }
  337. bit_buffer_append_bytes(passy_reader->DG1, chunk, Le);
  338. body_offset += Le;
  339. } while(body_offset < body_size);
  340. passy_log_bitbuffer(TAG, "DG1", passy_reader->DG1);
  341. } else if(passy->read_type == PassyReadDG2 || passy->read_type == PassyReadDG7) {
  342. uint8_t header[100];
  343. ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
  344. if(ret != NfcCommandContinue) {
  345. view_dispatcher_send_custom_event(
  346. passy->view_dispatcher, PassyCustomEventReaderError);
  347. break;
  348. }
  349. view_dispatcher_send_custom_event(
  350. passy->view_dispatcher, PassyCustomEventReaderReading);
  351. size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
  352. FURI_LOG_I(
  353. TAG, "%s length: %d", passy->read_type == PassyReadDG2 ? "DG2" : "DG7", body_size);
  354. void* jpeg = memmem(header, sizeof(header), jpeg_header, sizeof(jpeg_header));
  355. void* jpeg2k = memmem(header, sizeof(header), jpeg2k_header, sizeof(jpeg2k_header));
  356. void* jpeg2k_cs =
  357. memmem(header, sizeof(header), jpeg2k_cs_header, sizeof(jpeg2k_cs_header));
  358. FuriString* path = furi_string_alloc();
  359. uint8_t start = 0;
  360. const char* dg_type = passy->read_type == PassyReadDG2 ? "DG2" : "DG7";
  361. if(jpeg) {
  362. furi_string_printf(
  363. path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".jpeg");
  364. start = (uint8_t*)jpeg - header;
  365. } else if(jpeg2k) {
  366. furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".jp2");
  367. start = (uint8_t*)jpeg2k - header;
  368. } else if(jpeg2k_cs) {
  369. furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".jpc");
  370. start = (uint8_t*)jpeg2k_cs - header;
  371. } else {
  372. furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".bin");
  373. start = 0;
  374. passy_log_buffer(TAG, "header", header, sizeof(header));
  375. }
  376. FURI_LOG_I(TAG, "Writing offset %d to %s", start, furi_string_get_cstr(path));
  377. Storage* storage = furi_record_open(RECORD_STORAGE);
  378. Stream* stream = file_stream_alloc(storage);
  379. file_stream_open(stream, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_ALWAYS);
  380. uint8_t chunk[PASSY_READER_DG2_CHUNK_SIZE];
  381. passy->offset = start;
  382. passy->bytes_total = body_size;
  383. do {
  384. memset(chunk, 0, sizeof(chunk));
  385. uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - passy->offset));
  386. ret = passy_reader_read_binary(passy_reader, passy->offset, Le, chunk);
  387. if(ret != NfcCommandContinue) {
  388. view_dispatcher_send_custom_event(
  389. passy->view_dispatcher, PassyCustomEventReaderError);
  390. break;
  391. }
  392. passy->offset += Le;
  393. // passy_log_buffer(TAG, "chunk", chunk, sizeof(chunk));
  394. stream_write(stream, chunk, Le);
  395. view_dispatcher_send_custom_event(
  396. passy->view_dispatcher, PassyCustomEventReaderReading);
  397. } while(passy->offset < body_size);
  398. file_stream_close(stream);
  399. furi_record_close(RECORD_STORAGE);
  400. furi_string_free(path);
  401. }
  402. // Everything done
  403. ret = NfcCommandStop;
  404. view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderSuccess);
  405. } while(false);
  406. return ret;
  407. }
  408. NfcCommand passy_reader_poller_callback(NfcGenericEvent event, void* context) {
  409. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  410. PassyReader* passy_reader = context;
  411. NfcCommand ret = NfcCommandContinue;
  412. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  413. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  414. FURI_LOG_D(TAG, "iso14443_4b_event->type %i", iso14443_4b_event->type);
  415. passy_reader->iso14443_4b_poller = iso14443_4b_poller;
  416. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  417. view_dispatcher_send_custom_event(
  418. passy_reader->passy->view_dispatcher, PassyCustomEventReaderDetected);
  419. nfc_device_set_data(
  420. passy_reader->passy->nfc_device,
  421. NfcProtocolIso14443_4b,
  422. nfc_poller_get_data(passy_reader->passy->poller));
  423. ret = passy_reader_state_machine(passy_reader);
  424. furi_thread_set_current_priority(FuriThreadPriorityLowest);
  425. } else if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeError) {
  426. Iso14443_4bPollerEventData* data = iso14443_4b_event->data;
  427. Iso14443_4bError error = data->error;
  428. FURI_LOG_W(TAG, "Iso14443_4bError %i", error);
  429. switch(error) {
  430. case Iso14443_4bErrorNone:
  431. break;
  432. case Iso14443_4bErrorNotPresent:
  433. break;
  434. case Iso14443_4bErrorProtocol:
  435. ret = NfcCommandStop;
  436. break;
  437. case Iso14443_4bErrorTimeout:
  438. break;
  439. }
  440. }
  441. return ret;
  442. }