passy_scene_read_success.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #include "../passy_i.h"
  2. #include <dolphin/dolphin.h>
  3. #define ASN_EMIT_DEBUG 0
  4. #include <lib/asn1/DG1.h>
  5. #define TAG "PassySceneReadCardSuccess"
  6. // Thank you proxmark code for your passport parsing
  7. void passy_scene_read_success_on_enter(void* context) {
  8. Passy* passy = context;
  9. dolphin_deed(DolphinDeedNfcReadSuccess);
  10. notification_message(passy->notifications, &sequence_success);
  11. furi_string_reset(passy->text_box_store);
  12. FuriString* str = passy->text_box_store;
  13. if(passy->read_type == PassyReadDG1) {
  14. DG1_t* dg1 = 0;
  15. dg1 = calloc(1, sizeof *dg1);
  16. assert(dg1);
  17. asn_dec_rval_t rval = asn_decode(
  18. 0,
  19. ATS_DER,
  20. &asn_DEF_DG1,
  21. (void**)&dg1,
  22. bit_buffer_get_data(passy->DG1),
  23. bit_buffer_get_size_bytes(passy->DG1));
  24. if(rval.code == RC_OK) {
  25. FURI_LOG_I(TAG, "ASN.1 decode success");
  26. char payloadDebug[384] = {0};
  27. memset(payloadDebug, 0, sizeof(payloadDebug));
  28. (&asn_DEF_DG1)
  29. ->op->print_struct(&asn_DEF_DG1, dg1, 1, print_struct_callback, payloadDebug);
  30. if(strlen(payloadDebug) > 0) {
  31. FURI_LOG_D(TAG, "DG1: %s", payloadDebug);
  32. } else {
  33. FURI_LOG_D(TAG, "Received empty Payload");
  34. }
  35. if(dg1->mrz.buf[0] == 'I' && dg1->mrz.buf[1] == 'P') {
  36. furi_string_cat_printf(str, "Passport card\n");
  37. } else if(dg1->mrz.buf[0] == 'I') {
  38. furi_string_cat_printf(str, "ID Card\n");
  39. } else if(dg1->mrz.buf[0] == 'P') {
  40. furi_string_cat_printf(str, "Passport book\n");
  41. } else if(dg1->mrz.buf[0] == 'A') {
  42. furi_string_cat_printf(str, "Residency Permit\n");
  43. } else {
  44. furi_string_cat_printf(str, "Unknown\n");
  45. }
  46. uint8_t td_variant = 0;
  47. if(dg1->mrz.size == 90) {
  48. td_variant = 1;
  49. } else if(dg1->mrz.size == 88) {
  50. td_variant = 3;
  51. } else {
  52. FURI_LOG_W(TAG, "MRZ length (%zu) is unexpected.", dg1->mrz.size);
  53. }
  54. char name[40] = {0};
  55. memset(name, 0, sizeof(name));
  56. uint8_t name_offset = td_variant == 3 ? 5 : 60;
  57. memcpy(name, dg1->mrz.buf + name_offset, 38);
  58. // Work backwards replace < at the end with \0
  59. for(size_t i = sizeof(name) - 1; i > 0; i--) {
  60. if(name[i] == '<') {
  61. name[i] = '\0';
  62. } else {
  63. break;
  64. }
  65. }
  66. // Work forwards replace < with space
  67. for(size_t i = 0; i < sizeof(name); i++) {
  68. if(name[i] == '<') {
  69. name[i] = ' ';
  70. }
  71. }
  72. if(td_variant == 3) { // Passport form factor
  73. char* row_1 = (char*)dg1->mrz.buf + 0;
  74. char* row_2 = (char*)dg1->mrz.buf + 44;
  75. furi_string_cat_printf(str, "Country: %.3s\n", row_1 + 2);
  76. furi_string_cat_printf(str, "Name: %s\n", name);
  77. furi_string_cat_printf(str, "Doc Number: %.9s\n", row_2);
  78. furi_string_cat_printf(str, "DoB: %.6s\n", row_2 + 13);
  79. furi_string_cat_printf(str, "Sex: %.1s\n", row_2 + 20);
  80. furi_string_cat_printf(str, "Expiry: %.6s\n", row_2 + 21);
  81. furi_string_cat_printf(str, "\n");
  82. furi_string_cat_printf(str, "Raw data:\n");
  83. furi_string_cat_printf(str, "%.44s\n", row_1);
  84. furi_string_cat_printf(str, "%.44s\n", row_2);
  85. } else if(td_variant == 1) { // ID form factor
  86. char* row_1 = (char*)dg1->mrz.buf + 0;
  87. char* row_2 = (char*)dg1->mrz.buf + 30;
  88. char* row_3 = (char*)dg1->mrz.buf + 60;
  89. furi_string_cat_printf(str, "Country: %.3s\n", row_1 + 2);
  90. furi_string_cat_printf(str, "Name: %s\n", name);
  91. furi_string_cat_printf(str, "Doc Number: %.9s\n", row_1 + 5);
  92. furi_string_cat_printf(str, "DoB: %.6s\n", row_2);
  93. furi_string_cat_printf(str, "Sex: %.1s\n", row_2 + 7);
  94. furi_string_cat_printf(str, "Expiry: %.6s\n", row_2 + 8);
  95. furi_string_cat_printf(str, "\n");
  96. furi_string_cat_printf(str, "Raw data:\n");
  97. furi_string_cat_printf(str, "%.30s\n", row_1);
  98. furi_string_cat_printf(str, "%.30s\n", row_2);
  99. furi_string_cat_printf(str, "%.30s\n", row_3);
  100. }
  101. } else {
  102. FURI_LOG_E(TAG, "ASN.1 decode failed: %d. %d consumed", rval.code, rval.consumed);
  103. furi_string_cat_printf(str, "%s\n", bit_buffer_get_data(passy->DG1));
  104. }
  105. free(dg1);
  106. dg1 = 0;
  107. } else if(passy->read_type == PassyReadDG2 || passy->read_type == PassyReadDG7) {
  108. furi_string_cat_printf(str, "Saved to disk in apps_data/passy/\n");
  109. } else {
  110. char display[9]; // 4 byte header in hex + NULL
  111. memset(display, 0, sizeof(display));
  112. for(size_t i = 0; i < bit_buffer_get_size_bytes(passy->dg_header); i++) {
  113. snprintf(
  114. display + (i * 2),
  115. sizeof(display),
  116. "%02X",
  117. bit_buffer_get_data(passy->dg_header)[i]);
  118. }
  119. furi_string_cat_printf(str, "Unparsed file\n");
  120. furi_string_cat_printf(str, "File header: %s\n", display);
  121. }
  122. text_box_set_font(passy->text_box, TextBoxFontText);
  123. text_box_set_text(passy->text_box, furi_string_get_cstr(passy->text_box_store));
  124. view_dispatcher_switch_to_view(passy->view_dispatcher, PassyViewTextBox);
  125. }
  126. bool passy_scene_read_success_on_event(void* context, SceneManagerEvent event) {
  127. Passy* passy = context;
  128. bool consumed = false;
  129. if(event.type == SceneManagerEventTypeCustom) {
  130. } else if(event.type == SceneManagerEventTypeBack) {
  131. const uint32_t possible_scenes[] = {PassySceneAdvancedMenu, PassySceneMainMenu};
  132. scene_manager_search_and_switch_to_previous_scene_one_of(
  133. passy->scene_manager, possible_scenes, COUNT_OF(possible_scenes));
  134. consumed = true;
  135. }
  136. return consumed;
  137. }
  138. void passy_scene_read_success_on_exit(void* context) {
  139. Passy* passy = context;
  140. // Clear view
  141. text_box_reset(passy->text_box);
  142. }