passy_reader.c 22 KB

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