passy_scene_read_success.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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, "Issuing state: %.3s\n", row_1 + 2);
  76. furi_string_cat_printf(str, "Nationality: %.3s\n", row_2 + 10);
  77. furi_string_cat_printf(str, "Name: %s\n", name);
  78. furi_string_cat_printf(str, "Doc Number: %.9s\n", row_2);
  79. furi_string_cat_printf(str, "DoB: %.6s\n", row_2 + 13);
  80. furi_string_cat_printf(str, "Sex: %.1s\n", row_2 + 20);
  81. furi_string_cat_printf(str, "Expiry: %.6s\n", row_2 + 21);
  82. furi_string_cat_printf(str, "\n");
  83. furi_string_cat_printf(str, "Raw data:\n");
  84. furi_string_cat_printf(str, "%.44s\n", row_1);
  85. furi_string_cat_printf(str, "%.44s\n", row_2);
  86. } else if(td_variant == 1) { // ID form factor
  87. char* row_1 = (char*)dg1->mrz.buf + 0;
  88. char* row_2 = (char*)dg1->mrz.buf + 30;
  89. char* row_3 = (char*)dg1->mrz.buf + 60;
  90. furi_string_cat_printf(str, "Issuing state: %.3s\n", row_1 + 2);
  91. furi_string_cat_printf(str, "Nationality: %.3s\n", row_2 + 15);
  92. furi_string_cat_printf(str, "Name: %s\n", name);
  93. furi_string_cat_printf(str, "Doc Number: %.9s\n", row_1 + 5);
  94. furi_string_cat_printf(str, "DoB: %.6s\n", row_2);
  95. furi_string_cat_printf(str, "Sex: %.1s\n", row_2 + 7);
  96. furi_string_cat_printf(str, "Expiry: %.6s\n", row_2 + 8);
  97. furi_string_cat_printf(str, "\n");
  98. furi_string_cat_printf(str, "Raw data:\n");
  99. furi_string_cat_printf(str, "%.30s\n", row_1);
  100. furi_string_cat_printf(str, "%.30s\n", row_2);
  101. furi_string_cat_printf(str, "%.30s\n", row_3);
  102. }
  103. } else {
  104. FURI_LOG_E(TAG, "ASN.1 decode failed: %d. %d consumed", rval.code, rval.consumed);
  105. furi_string_cat_printf(str, "%s\n", bit_buffer_get_data(passy->DG1));
  106. }
  107. free(dg1);
  108. dg1 = 0;
  109. } else if(passy->read_type == PassyReadDG2 || passy->read_type == PassyReadDG7) {
  110. furi_string_cat_printf(str, "Saved to disk in apps_data/passy/...\n");
  111. } else {
  112. char display[9]; // 4 byte header in hex + NULL
  113. memset(display, 0, sizeof(display));
  114. for(size_t i = 0; i < bit_buffer_get_size_bytes(passy->dg_header); i++) {
  115. snprintf(
  116. display + (i * 2),
  117. sizeof(display),
  118. "%02X",
  119. bit_buffer_get_data(passy->dg_header)[i]);
  120. }
  121. furi_string_cat_printf(str, "Unparsed file\n");
  122. furi_string_cat_printf(str, "File header: %s\n", display);
  123. }
  124. text_box_set_font(passy->text_box, TextBoxFontText);
  125. text_box_set_text(passy->text_box, furi_string_get_cstr(passy->text_box_store));
  126. view_dispatcher_switch_to_view(passy->view_dispatcher, PassyViewTextBox);
  127. }
  128. bool passy_scene_read_success_on_event(void* context, SceneManagerEvent event) {
  129. Passy* passy = context;
  130. bool consumed = false;
  131. if(event.type == SceneManagerEventTypeCustom) {
  132. } else if(event.type == SceneManagerEventTypeBack) {
  133. const uint32_t possible_scenes[] = {PassySceneAdvancedMenu, PassySceneMainMenu};
  134. scene_manager_search_and_switch_to_previous_scene_one_of(
  135. passy->scene_manager, possible_scenes, COUNT_OF(possible_scenes));
  136. consumed = true;
  137. }
  138. return consumed;
  139. }
  140. void passy_scene_read_success_on_exit(void* context) {
  141. Passy* passy = context;
  142. // Clear view
  143. text_box_reset(passy->text_box);
  144. }