NFCPassportReader

repository·main·Indexed 21 days ago

https://github.com/andyq/nfcpassportreader

An iOS library for reading and verifying NFC-enabled passports using CoreNFC APIs. It supports security protocols including BAC, PACE, and Active/Passive Authentication. The library allows reading specific datagroups (such as DG1, DG2, and SOD) and provides tools for generating MRZ keys and processing CSCA certificates via a Python-based master list generator script.

Tokens
1.7K
Snippets
5
Records
12
Agent score
75%

What's inside NFCPassportReader

  1. Use Passive Authentication to verify e-Passports

    main

    Passive Authentication is used to ensure an e-Passport is valid and has not been tampered with.

    Requirement: You must provide a set of CSCA certificates in PEM format. These should be sourced from a master list (e.g., from a national authority or the ICAO PKD repository).

  2. Generate an MRZ Key for passport reading

    main

    To read a passport, you must first construct a mrzKey. This key is a concatenated string consisting of the passport number, date of birth, and expiry date, including their respective checksums.

    Format: <passport number><passport number checksum><date of birth><date of birth checksum><expiry date><expiry date checksum>

    Date Format: Dates must be in YYMMDD format.

    Example: For Passport 12345678, DOB 27-Jan-1998, and Expiry 30-Aug-2025:

    • Passport number: 12345678
    • Passport number checksum: 8
    • Date of birth: 980127
    • Date of birth checksum: 7
    • Expiry date: 250830
    • Expiry date checksum: 5

    Resulting mrzKey: "12345678898012772508315"

  3. Obtain ICAO PKD master lists

    main

    ICAO master lists are used as input for the certificate extraction script. While ICAO makes them freely available, the download process is manual and cannot be automated.

    Alternatively, some countries (e.g., Germany, France, Italy) provide their own masterlists. You can search for <country> masterlist or check the JMRTD project certificates page.

  4. Generate a PEM master list using the Master list generator script

    main

    The extract.py script generates a file containing unique CSCA (Country Signing Certificate Authority) certificates in PEM format. These certificates are used to verify Document Signing certificates and Security Object DS Signatures (SOD) in e-passports and electronic identity cards via Passive Authentication in the NFCPassportReader app.

    Input Formats

    The script accepts one of the following:

    • An ICAO PKD master list file in LDIF format (a collection of master lists from validated countries).
    • A single Country masterlist file in Cryptographic Message Syntax (CMS) format (typically extracted from a .zip file, resulting in a <file>.ml).

    Output

    The script produces a file named masterList.pem, which is a concatenation of all unique certificates found in the input.

    Requirements

    • Python 3.7 (or other Python 3 versions).
    • OpenSSL with CMS flag support. Note: The default OpenSSL provided with macOS (including Catalina) does not support this; you may need to install a version via Homebrew.
    python extract.py [Country master list.ml|ICAO LDIF file]
    
    # Example usage with an LDIF file:
    python extract.py icaopkd-002-ml-000119.ldif
  5. Install NFCPassportReader via CocoaPods

    main

    CocoaPods is deprecated and unsupported. If you must use it, add the following to your Podfile. Note: Do not use Bitcode, as it is not supported and has been deprecated by Apple.

    use_frameworks!
    pod 'NFCPassportReader', git:'https://github.com/AndyQ/NFCPassportReader.git'  

    Then run:

    $ pod install
  6. Troubleshoot MRZ Key errors (SW1: 0x63, SW2: 0x00)

    main

    If you encounter a Mutual Authentication error with SW1 code 0x63 and SW2 code 0x00 ("No information given"), it usually indicates an incorrect mrzKey.

    Special Case: Padding Characters If the passport number in the MRZ contains a < character, you must include this character in the mrzKey used for BAC.

    Example: If the MRZ bottom line is 12345678<8AUT7005233M2507237<<<<<<<<<<<<<<06, use 12345678<870052332507237 as the key.

  7. Explore the NFCPassportReaderApp sample application

    main

    The NFCPassportReaderApp is a demonstration project designed to show how to read an e-passport using the NFCPassportReader library.

    Key features of the sample app:

    • Manual Data Entry: Users must manually enter the passport number, date of birth, and passport expiry date.
    • Checksum Calculation: The app automatically calculates checksums based on the provided manual input.
    • Persistence: The app saves the last entered passport details to UserDefaults to prevent re-entry during subsequent sessions.

    Note: While the sample app successfully reads the author's passport, performance and compatibility may vary depending on the specific passport being scanned.

  8. Customize NFC session display messages

    main

    You can override the messages shown to the user during the NFC session by providing a customDisplayMessage callback to the readPassport method. This allows you to provide specific instructions, such as telling the user to hold their phone near the passport.

    passportReader.readPassport(mrzKey: mrzKey, tags: [.COM, .DG1, .DG2],
        customDisplayMessage: { (displayMessage) in
            switch displayMessage {
            case .requestPresentPassport:
                return "Hold your iPhone near an NFC enabled passport."
            default: 
                return nil
        },
        completed: { (error) in
            ...
        }
    )
  9. Read a passport using readPassport()

    main

    Call readPassport on an instance of PassportReader. You must provide the mrzKey, the desired tags (datagroups), and a completion block.

    Upon success, the passportReader instance will be populated with data in the passportMRZ and passportImage fields. Note that JPEG2000 images are currently unsupported, though raw data is accessible.

    Supported Datagroups (tags): COM, DG1, DG2, DG7, DG11, DG12, DG14 (partial), DG15, and SOD.

    passportReader.readPassport(mrzKey: mrzKey, tags: [.COM, .DG1, .DG2], completed: { (error) in
       // Handle error or success
    })
  10. Enable Extended Mode for reading

    main
    Some passports (e.g., certain Australian passports) require more bytes to be read in a single call, especially when using long Active Authentication (AA) keys. You can enable this by passing the useExtendedMode flag to the readPassport function.