Selaa lähdekoodia

Merge passy from https://github.com/bettse/passy

Willy-JL 8 kuukautta sitten
vanhempi
commit
6611b64919

+ 7 - 0
passy/.catalog/README.md

@@ -6,9 +6,16 @@
 🇬🇧
 🇵🇭
 🇷🇺
+🇹🇼
+🇺🇦
+🇦🇿
 
 (If it works for yours, submit a PR to add your country flag)
 
+## To use:
+
+eMTRD are secured to prevent people from reading the data on a passport just by bumping into it.  The data is secured using a key based on the passport number, date of birth, and date of expiry.  A real passport machine reads these values from the MRZ (Machine Readable Zone, the ones with ">") using a camera. For the app, you have to enter the values manually.  The app will then generate the key and read the data using a system called BAC(https://en.wikipedia.org/wiki/Basic_access_control).
+
 ## Notes:
  - Caches MRZ info to make subsequent use faster
 

+ 3 - 0
passy/.catalog/changelog.md

@@ -1,5 +1,8 @@
 ## 1.3
  - Show both issuing state and nationality (Thanks @portasynthinca3)
+ - Handle Passport numbers < 9 char
+ - Fix bug with SELECT AID APDU Le
+ - New Icon (Thank you @equipter for the idea)
 ## 1.2
  - Fix bug with detecting wrong protocol
  - Fix bug with check number of alpha characters (Thanks @WillyJL)

BIN
passy/.catalog/screenshots/menu.png


+ 6 - 2
passy/README.md

@@ -7,11 +7,15 @@
 🇬🇧
 🇵🇭
 🇷🇺
+🇹🇼
+🇺🇦
+🇦🇿
 
 (If it works for yours, submit a PR to add your country flag)
 
-## Notes:
- - Caches MRZ info to make subsequent use faster
+## To use:
+
+eMTRD are secured to prevent people from reading the data on a passport just by bumping into it.  The data is secured using a key based on the passport number, date of birth, and date of expiry.  A real passport machine reads these values from the MRZ (Machine Readable Zone, the ones with ">") using a camera. For the app, you have to enter the values manually.  The app will then generate the key and read the data using a system called BAC(https://en.wikipedia.org/wiki/Basic_access_control).
 
 ## Limitations
  - Does not parse some of the optional DG (under 'advanced' menu)

+ 1 - 1
passy/application.fam

@@ -8,7 +8,7 @@ App(
     stack_size=2 * 1024,
     fap_category="NFC",
     # Optional values
-    fap_version="1.2",
+    fap_version="1.3",
     fap_icon="passy.png",  # 10x10 1-bit PNG
     fap_description="eMRTD Reader",
     fap_author="bettse",

BIN
passy/images/EPassportLogo_97x61.png


BIN
passy/images/EPassport_logo.svg.png


BIN
passy/passy.png


+ 181 - 135
passy/passy_reader.c

@@ -1,11 +1,13 @@
 #include "passy_reader.h"
 
+#define ASN_EMIT_DEBUG 0
+#include <lib/asn1/COM.h>
+
 #define TAG                         "PassyReader"
 #define PASSY_READER_DG1_CHUNK_SIZE 0x20
 #define PASSY_READER_DG2_CHUNK_SIZE 0x20
 
-#define ASN_EMIT_DEBUG 0
-#include <lib/asn1/COM.h>
+#define PASSY_DG2_LOOKAHEAD 120
 
 static uint8_t passport_aid[] = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01};
 static uint8_t select_header[] = {0x00, 0xA4, 0x04, 0x0C};
@@ -18,6 +20,8 @@ static const uint8_t jpeg_header[4] = {0xFF, 0xD8, 0xFF, 0xE0};
 static const uint8_t jpeg2k_header[6] = {0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50};
 static const uint8_t jpeg2k_cs_header[4] = {0xFF, 0x4F, 0xFF, 0x51};
 
+static bool print_logs = true;
+
 size_t asn1_length(uint8_t data[3]) {
     if(data[0] <= 0x7F) {
         return data[0];
@@ -95,25 +99,34 @@ NfcCommand passy_reader_send(PassyReader* passy_reader) {
         Iso14443_4aPoller* iso14443_4a_poller = passy_reader->iso14443_4a_poller;
         Iso14443_4aError error;
 
-        passy_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
+        if(print_logs) {
+            passy_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
+        }
         error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
         if(error != Iso14443_4aErrorNone) {
             FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error);
             return NfcCommandStop;
         }
-    } else {
+    } else if(strcmp(passy->proto, "4b") == 0) {
         Iso14443_4bPoller* iso14443_4b_poller = passy_reader->iso14443_4b_poller;
         Iso14443_4bError error;
 
-        passy_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
+        if(print_logs) {
+            passy_log_bitbuffer(TAG, "NFC transmit", tx_buffer);
+        }
         error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
         if(error != Iso14443_4bErrorNone) {
             FURI_LOG_W(TAG, "iso14443_4b_poller_send_block error %d", error);
             return NfcCommandStop;
         }
+    } else {
+        FURI_LOG_W(TAG, "Unknown protocol %s", passy->proto);
+        return NfcCommandStop;
     }
     bit_buffer_reset(tx_buffer);
-    passy_log_bitbuffer(TAG, "NFC response", rx_buffer);
+    if(print_logs) {
+        passy_log_bitbuffer(TAG, "NFC response", rx_buffer);
+    }
 
     // Check SW
     size_t length = bit_buffer_get_size_bytes(rx_buffer);
@@ -139,7 +152,6 @@ NfcCommand passy_reader_select_application(PassyReader* passy_reader) {
     bit_buffer_append_bytes(tx_buffer, select_header, sizeof(select_header));
     bit_buffer_append_byte(tx_buffer, sizeof(passport_aid));
     bit_buffer_append_bytes(tx_buffer, passport_aid, sizeof(passport_aid));
-    bit_buffer_append_byte(tx_buffer, 0x00); // Le
     ret = passy_reader_send(passy_reader);
     if(ret != NfcCommandContinue) {
         return ret;
@@ -205,7 +217,7 @@ NfcCommand passy_reader_authenticate(PassyReader* passy_reader) {
     bit_buffer_append_byte(tx_buffer, sizeof(eifd) + sizeof(mifd));
     bit_buffer_append_bytes(tx_buffer, eifd, sizeof(eifd));
     bit_buffer_append_bytes(tx_buffer, mifd, sizeof(mifd));
-    bit_buffer_append_byte(tx_buffer, 0x28); // Le
+    bit_buffer_append_byte(tx_buffer, 0); // Le
 
     ret = passy_reader_send(passy_reader);
     if(ret != NfcCommandContinue) {
@@ -233,7 +245,10 @@ NfcCommand passy_reader_authenticate(PassyReader* passy_reader) {
         mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length - 2 - 8, iv, data, decrypted);
         mbedtls_des3_free(&ctx);
     } while(false);
-    passy_log_buffer(TAG, "decrypted", decrypted, sizeof(decrypted));
+
+    if(print_logs) {
+        passy_log_buffer(TAG, "decrypted", decrypted, sizeof(decrypted));
+    }
 
     uint8_t* rnd_icc = decrypted;
     uint8_t* rnd_ifd = decrypted + 8;
@@ -267,7 +282,9 @@ NfcCommand passy_reader_select_file(PassyReader* passy_reader, uint16_t file_id)
     }
 
     secure_messaging_unwrap_rapdu(passy_reader->secure_messaging, passy_reader->rx_buffer);
-    passy_log_bitbuffer(TAG, "NFC response (decrypted)", passy_reader->rx_buffer);
+    if(print_logs) {
+        passy_log_bitbuffer(TAG, "NFC response (decrypted)", passy_reader->rx_buffer);
+    }
 
     return ret;
 }
@@ -293,13 +310,156 @@ NfcCommand passy_reader_read_binary(
     }
 
     secure_messaging_unwrap_rapdu(passy_reader->secure_messaging, passy_reader->rx_buffer);
-    passy_log_bitbuffer(TAG, "NFC response (decrypted)", passy_reader->rx_buffer);
+    if(print_logs) {
+        passy_log_bitbuffer(TAG, "NFC response (decrypted)", passy_reader->rx_buffer);
+    }
     const uint8_t* decrypted_data = bit_buffer_get_data(passy_reader->rx_buffer);
     memcpy(output_buffer, decrypted_data, Le);
 
     return ret;
 }
 
+NfcCommand passy_reader_read_com(PassyReader* passy_reader) {
+    NfcCommand ret = NfcCommandContinue;
+    Passy* passy = passy_reader->passy;
+
+    bit_buffer_reset(passy_reader->COM);
+    uint8_t header[4];
+    ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
+    if(ret != NfcCommandContinue) {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+        return ret;
+    }
+    size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
+    uint8_t body_offset = sizeof(header);
+    bit_buffer_append_bytes(passy_reader->COM, header, sizeof(header));
+    do {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderReading);
+        uint8_t chunk[PASSY_READER_DG1_CHUNK_SIZE];
+        uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - body_offset));
+
+        ret = passy_reader_read_binary(passy_reader, body_offset, Le, chunk);
+        if(ret != NfcCommandContinue) {
+            view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+            break;
+        }
+        bit_buffer_append_bytes(passy_reader->COM, chunk, Le);
+        body_offset += Le;
+    } while(body_offset < body_size);
+
+    return ret;
+}
+
+NfcCommand passy_reader_read_dg1(PassyReader* passy_reader) {
+    NfcCommand ret = NfcCommandContinue;
+    Passy* passy = passy_reader->passy;
+    bit_buffer_reset(passy->DG1);
+    uint8_t header[4];
+    ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
+    if(ret != NfcCommandContinue) {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+
+        return ret;
+    }
+    size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
+    uint8_t body_offset = sizeof(header);
+    bit_buffer_append_bytes(passy_reader->DG1, header, sizeof(header));
+    do {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderReading);
+        uint8_t chunk[PASSY_READER_DG1_CHUNK_SIZE];
+        uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - body_offset));
+
+        ret = passy_reader_read_binary(passy_reader, body_offset, Le, chunk);
+        if(ret != NfcCommandContinue) {
+            view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+            break;
+        }
+        bit_buffer_append_bytes(passy_reader->DG1, chunk, Le);
+        body_offset += Le;
+    } while(body_offset < body_size);
+    passy_log_bitbuffer(TAG, "DG1", passy_reader->DG1);
+
+    return ret;
+}
+
+NfcCommand passy_reader_read_dg2_or_dg7(PassyReader* passy_reader) {
+    NfcCommand ret = NfcCommandContinue;
+    Passy* passy = passy_reader->passy;
+
+    uint8_t header[PASSY_DG2_LOOKAHEAD];
+    ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header) / 2, header);
+    if(ret != NfcCommandContinue) {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+        return ret;
+    }
+    // Issue with reading whole 120 bytes at once, so we read 60 bytes and then
+    ret = passy_reader_read_binary(
+        passy_reader, sizeof(header) / 2, sizeof(header) / 2, header + sizeof(header) / 2);
+    if(ret != NfcCommandContinue) {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+        return ret;
+    }
+
+    view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderReading);
+
+    size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
+    FURI_LOG_I(TAG, "%s length: %d", passy->read_type == PassyReadDG2 ? "DG2" : "DG7", body_size);
+
+    void* jpeg = memmem(header, sizeof(header), jpeg_header, sizeof(jpeg_header));
+    void* jpeg2k = memmem(header, sizeof(header), jpeg2k_header, sizeof(jpeg2k_header));
+    void* jpeg2k_cs = memmem(header, sizeof(header), jpeg2k_cs_header, sizeof(jpeg2k_cs_header));
+
+    FuriString* path = furi_string_alloc();
+    uint8_t start = 0;
+    const char* dg_type = passy->read_type == PassyReadDG2 ? "DG2" : "DG7";
+    char* dg_ext = ".bin";
+
+    if(jpeg) {
+        dg_ext = ".jpeg";
+        start = (uint8_t*)jpeg - header;
+    } else if(jpeg2k) {
+        dg_ext = ".jp2";
+        start = (uint8_t*)jpeg2k - header;
+    } else if(jpeg2k_cs) {
+        dg_ext = ".jpc";
+        start = (uint8_t*)jpeg2k_cs - header;
+    } else {
+        furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".bin");
+        start = 0;
+        passy_log_buffer(TAG, "header", header, sizeof(header));
+    }
+    furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, dg_ext);
+    FURI_LOG_I(TAG, "Writing offset %d to %s", start, furi_string_get_cstr(path));
+
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    Stream* stream = file_stream_alloc(storage);
+    file_stream_open(stream, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_ALWAYS);
+
+    uint8_t chunk[PASSY_READER_DG2_CHUNK_SIZE];
+    passy->offset = start;
+    passy->bytes_total = body_size;
+    do {
+        memset(chunk, 0, sizeof(chunk));
+        uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - passy->offset));
+
+        ret = passy_reader_read_binary(passy_reader, passy->offset, Le, chunk);
+        if(ret != NfcCommandContinue) {
+            view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderError);
+            break;
+        }
+        passy->offset += Le;
+        // passy_log_buffer(TAG, "chunk", chunk, sizeof(chunk));
+        stream_write(stream, chunk, Le);
+        view_dispatcher_send_custom_event(passy->view_dispatcher, PassyCustomEventReaderReading);
+    } while(passy->offset < body_size);
+
+    file_stream_close(stream);
+    furi_record_close(RECORD_STORAGE);
+    furi_string_free(path);
+
+    return ret;
+}
+
 NfcCommand passy_reader_state_machine(PassyReader* passy_reader) {
     furi_assert(passy_reader);
     Passy* passy = passy_reader->passy;
@@ -333,131 +493,17 @@ NfcCommand passy_reader_state_machine(PassyReader* passy_reader) {
         }
 
         if(passy->read_type == PassyReadCOM) {
-            bit_buffer_reset(passy_reader->COM);
-            uint8_t header[4];
-            ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
-            if(ret != NfcCommandContinue) {
-                view_dispatcher_send_custom_event(
-                    passy->view_dispatcher, PassyCustomEventReaderError);
-
-                break;
-            }
-            size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
-            uint8_t body_offset = sizeof(header);
-            bit_buffer_append_bytes(passy_reader->COM, header, sizeof(header));
-            do {
-                view_dispatcher_send_custom_event(
-                    passy->view_dispatcher, PassyCustomEventReaderReading);
-                uint8_t chunk[PASSY_READER_DG1_CHUNK_SIZE];
-                uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - body_offset));
-
-                ret = passy_reader_read_binary(passy_reader, body_offset, Le, chunk);
-                if(ret != NfcCommandContinue) {
-                    view_dispatcher_send_custom_event(
-                        passy->view_dispatcher, PassyCustomEventReaderError);
-                    break;
-                }
-                bit_buffer_append_bytes(passy_reader->COM, chunk, Le);
-                body_offset += Le;
-            } while(body_offset < body_size);
-
+            ret = passy_reader_read_com(passy_reader);
         } else if(passy->read_type == PassyReadDG1) {
-            bit_buffer_reset(passy->DG1);
-            uint8_t header[4];
-            ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
-            if(ret != NfcCommandContinue) {
-                view_dispatcher_send_custom_event(
-                    passy->view_dispatcher, PassyCustomEventReaderError);
-
-                break;
-            }
-            size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
-            uint8_t body_offset = sizeof(header);
-            bit_buffer_append_bytes(passy_reader->DG1, header, sizeof(header));
-            do {
-                view_dispatcher_send_custom_event(
-                    passy->view_dispatcher, PassyCustomEventReaderReading);
-                uint8_t chunk[PASSY_READER_DG1_CHUNK_SIZE];
-                uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - body_offset));
-
-                ret = passy_reader_read_binary(passy_reader, body_offset, Le, chunk);
-                if(ret != NfcCommandContinue) {
-                    view_dispatcher_send_custom_event(
-                        passy->view_dispatcher, PassyCustomEventReaderError);
-                    break;
-                }
-                bit_buffer_append_bytes(passy_reader->DG1, chunk, Le);
-                body_offset += Le;
-            } while(body_offset < body_size);
-            passy_log_bitbuffer(TAG, "DG1", passy_reader->DG1);
-        } else if(passy->read_type == PassyReadDG2 || passy->read_type == PassyReadDG7) {
-            uint8_t header[100];
-            ret = passy_reader_read_binary(passy_reader, 0x00, sizeof(header), header);
-            if(ret != NfcCommandContinue) {
-                view_dispatcher_send_custom_event(
-                    passy->view_dispatcher, PassyCustomEventReaderError);
-                break;
-            }
-            view_dispatcher_send_custom_event(
-                passy->view_dispatcher, PassyCustomEventReaderReading);
-
-            size_t body_size = 1 + asn1_length_length(header + 1) + asn1_length(header + 1);
-            FURI_LOG_I(
-                TAG, "%s length: %d", passy->read_type == PassyReadDG2 ? "DG2" : "DG7", body_size);
-
-            void* jpeg = memmem(header, sizeof(header), jpeg_header, sizeof(jpeg_header));
-            void* jpeg2k = memmem(header, sizeof(header), jpeg2k_header, sizeof(jpeg2k_header));
-            void* jpeg2k_cs =
-                memmem(header, sizeof(header), jpeg2k_cs_header, sizeof(jpeg2k_cs_header));
-
-            FuriString* path = furi_string_alloc();
-            uint8_t start = 0;
-            const char* dg_type = passy->read_type == PassyReadDG2 ? "DG2" : "DG7";
-
-            if(jpeg) {
-                furi_string_printf(
-                    path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".jpeg");
-                start = (uint8_t*)jpeg - header;
-            } else if(jpeg2k) {
-                furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".jp2");
-                start = (uint8_t*)jpeg2k - header;
-            } else if(jpeg2k_cs) {
-                furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".jpc");
-                start = (uint8_t*)jpeg2k_cs - header;
-            } else {
-                furi_string_printf(path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dg_type, ".bin");
-                start = 0;
-                passy_log_buffer(TAG, "header", header, sizeof(header));
-            }
-            FURI_LOG_I(TAG, "Writing offset %d to %s", start, furi_string_get_cstr(path));
-
-            Storage* storage = furi_record_open(RECORD_STORAGE);
-            Stream* stream = file_stream_alloc(storage);
-            file_stream_open(stream, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_ALWAYS);
-
-            uint8_t chunk[PASSY_READER_DG2_CHUNK_SIZE];
-            passy->offset = start;
-            passy->bytes_total = body_size;
-            do {
-                memset(chunk, 0, sizeof(chunk));
-                uint8_t Le = MIN(sizeof(chunk), (size_t)(body_size - passy->offset));
-
-                ret = passy_reader_read_binary(passy_reader, passy->offset, Le, chunk);
-                if(ret != NfcCommandContinue) {
-                    view_dispatcher_send_custom_event(
-                        passy->view_dispatcher, PassyCustomEventReaderError);
-                    break;
-                }
-                passy->offset += Le;
-                // passy_log_buffer(TAG, "chunk", chunk, sizeof(chunk));
-                stream_write(stream, chunk, Le);
-                view_dispatcher_send_custom_event(
-                    passy->view_dispatcher, PassyCustomEventReaderReading);
-            } while(passy->offset < body_size);
-
-            file_stream_close(stream);
-            furi_record_close(RECORD_STORAGE);
-            furi_string_free(path);
+            ret = passy_reader_read_dg1(passy_reader);
+        } else if(passy->read_type == PassyReadDG2) {
+            print_logs = false;
+            ret = passy_reader_read_dg2_or_dg7(passy_reader);
+            print_logs = true;
+        } else if(passy->read_type == PassyReadDG7) {
+            print_logs = false;
+            ret = passy_reader_read_dg2_or_dg7(passy_reader);
+            print_logs = true;
         } else {
             // Until file specific handling is implemented, we just read the header
             bit_buffer_reset(passy_reader->dg_header);

+ 65 - 0
passy/scenes/passy_scene_adv_warning.c

@@ -0,0 +1,65 @@
+#include "../passy_i.h"
+
+#define TAG "PassySceneReadCardSuccess"
+
+void passy_scene_adv_warning_widget_callback(GuiButtonType result, InputType type, void* context) {
+    furi_assert(context);
+    Passy* passy = context;
+
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(passy->view_dispatcher, result);
+    }
+}
+
+void passy_scene_adv_warning_on_enter(void* context) {
+    Passy* passy = context;
+    Widget* widget = passy->widget;
+
+    FuriString* first_str = furi_string_alloc_set("These DG may require");
+    FuriString* second_str = furi_string_alloc_set("advanced authentication.\n");
+    FuriString* third_str = furi_string_alloc_set("Do not expect them to work.\n");
+    FuriString* fourth_str = furi_string_alloc_set("Do not open issues for them.\n");
+
+    widget_add_string_element(
+        widget, 64, 8, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(first_str));
+    widget_add_string_element(
+        widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(second_str));
+    widget_add_string_element(
+        widget, 0, 32, AlignLeft, AlignCenter, FontSecondary, furi_string_get_cstr(third_str));
+    widget_add_string_element(
+        widget, 0, 44, AlignLeft, AlignCenter, FontSecondary, furi_string_get_cstr(fourth_str));
+
+    widget_add_button_element(
+        widget, GuiButtonTypeCenter, "OK", passy_scene_adv_warning_widget_callback, passy);
+
+    furi_string_free(first_str);
+    furi_string_free(second_str);
+    furi_string_free(third_str);
+    furi_string_free(fourth_str);
+
+    view_dispatcher_switch_to_view(passy->view_dispatcher, PassyViewWidget);
+}
+
+bool passy_scene_adv_warning_on_event(void* context, SceneManagerEvent event) {
+    Passy* passy = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            consumed = scene_manager_previous_scene(passy->scene_manager);
+        } else if(event.event == GuiButtonTypeCenter) {
+            passy->read_type = PassyReadCOM;
+            scene_manager_next_scene(passy->scene_manager, PassySceneRead);
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_previous_scene(passy->scene_manager);
+    }
+    return consumed;
+}
+
+void passy_scene_adv_warning_on_exit(void* context) {
+    Passy* passy = context;
+
+    // Clear view
+    widget_reset(passy->widget);
+}

+ 2 - 0
passy/scenes/passy_scene_config.h

@@ -8,3 +8,5 @@ ADD_SCENE(passy, doe_input, DoEInput)
 ADD_SCENE(passy, delete, Delete)
 ADD_SCENE(passy, delete_success, DeleteSuccess)
 ADD_SCENE(passy, advanced_menu, AdvancedMenu)
+ADD_SCENE(passy, known_issues, KnownIssues)
+ADD_SCENE(passy, adv_warning, AdvWarning)

+ 40 - 0
passy/scenes/passy_scene_known_issues.c

@@ -0,0 +1,40 @@
+#include "../passy_i.h"
+
+static const char* known_issues_text = "Passy only uses BAC, not PACE for auth\n"
+                                       "PACE-only:\n"
+                                       "  - German Personalausweis\n"
+                                       "  - German Aufenthaltstitel post 2015\n";
+
+void passy_scene_known_issues_on_enter(void* context) {
+    Passy* passy = context;
+
+    furi_string_reset(passy->text_box_store);
+
+    FuriString* str = passy->text_box_store;
+    furi_string_cat_printf(str, "%s\n", known_issues_text);
+
+    text_box_set_font(passy->text_box, TextBoxFontText);
+    text_box_set_text(passy->text_box, furi_string_get_cstr(passy->text_box_store));
+    view_dispatcher_switch_to_view(passy->view_dispatcher, PassyViewTextBox);
+}
+
+bool passy_scene_known_issues_on_event(void* context, SceneManagerEvent event) {
+    Passy* passy = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            consumed = scene_manager_previous_scene(passy->scene_manager);
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_previous_scene(passy->scene_manager);
+    }
+    return consumed;
+}
+
+void passy_scene_known_issues_on_exit(void* context) {
+    Passy* passy = context;
+
+    // Clear views
+    text_box_reset(passy->text_box);
+}

+ 16 - 3
passy/scenes/passy_scene_main_menu.c

@@ -7,6 +7,7 @@ enum SubmenuIndex {
     SubmenuIndexReadDG1,
     SubmenuIndexReadDG2,
     SubmenuIndexReadAdvanced,
+    SubmenuIndexKnownIssues,
     SubmenuIndexDeleteMRZInfo,
 };
 
@@ -42,12 +43,21 @@ void passy_scene_main_menu_on_enter(void* context) {
             SubmenuIndexReadDG2,
             passy_scene_main_menu_submenu_callback,
             passy);
+        if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+            submenu_add_item(
+                submenu,
+                "Read Advanced",
+                SubmenuIndexReadAdvanced,
+                passy_scene_main_menu_submenu_callback,
+                passy);
+        }
         submenu_add_item(
             submenu,
-            "Read Advanced",
-            SubmenuIndexReadAdvanced,
+            "Known Issues",
+            SubmenuIndexKnownIssues,
             passy_scene_main_menu_submenu_callback,
             passy);
+
         submenu_add_item(
             submenu,
             "Delete MRZ Info",
@@ -83,7 +93,10 @@ bool passy_scene_main_menu_on_event(void* context, SceneManagerEvent event) {
             consumed = true;
         } else if(event.event == SubmenuIndexReadAdvanced) {
             passy->read_type = PassyReadCOM;
-            scene_manager_next_scene(passy->scene_manager, PassySceneRead);
+            scene_manager_next_scene(passy->scene_manager, PassySceneAdvWarning);
+            consumed = true;
+        } else if(event.event == SubmenuIndexKnownIssues) {
+            scene_manager_next_scene(passy->scene_manager, PassySceneKnownIssues);
             consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {

+ 7 - 1
passy/scenes/passy_scene_passport_number_input.c

@@ -16,7 +16,6 @@ void passy_scene_passport_number_input_on_enter(void* context) {
     TextInput* text_input = passy->text_input;
 
     text_input_set_header_text(text_input, "Passport Number");
-    text_input_set_minimum_length(text_input, 9);
     if(passy->passport_number[0] != '\0') {
         strlcpy(passy->text_store, passy->passport_number, sizeof(passy->text_store));
     }
@@ -37,6 +36,13 @@ bool passy_scene_passport_number_input_on_event(void* context, SceneManagerEvent
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == PassyCustomEventTextInputDone) {
+            // Padding the passport number with '<' to make it 9 characters long
+            size_t len = strlen(passy->text_store);
+            while(len < 9) {
+                passy->text_store[len++] = '<';
+            }
+            passy->text_store[len] = '\0';
+
             strlcpy(passy->passport_number, passy->text_store, strlen(passy->text_store) + 1);
             scene_manager_next_scene(passy->scene_manager, PassySceneDoBInput);
             consumed = true;

+ 1 - 1
passy/scenes/passy_scene_read_success.c

@@ -125,7 +125,7 @@ void passy_scene_read_success_on_enter(void* context) {
         dg1 = 0;
 
     } else if(passy->read_type == PassyReadDG2 || passy->read_type == PassyReadDG7) {
-        furi_string_cat_printf(str, "Saved to disk in apps_data/passy/\n");
+        furi_string_cat_printf(str, "Saved to disk in apps_data/passy/...\n");
     } else {
         char display[9]; // 4 byte header in hex + NULL
         memset(display, 0, sizeof(display));