### Install with Swift Package Manager Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/README.md Use Swift Package Manager to add the NFCPassportReader library to your project. Ensure you select the main branch and version 2.x with async/await support. ```bash File → Add Packages → Enter: https://github.com/AndyQ/NFCPassportReader.git ``` -------------------------------- ### MRZ Key Example Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/00-START-HERE.md An example of the Machine Readable Zone (MRZ) key format derived from a passport's printed data page. Refer to quick-start.md for detailed format. ```text Example: "12345678898012772508315" ``` -------------------------------- ### Install via CocoaPods (Deprecated) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md Install the NFCPassportReader library using CocoaPods. Note that CocoaPods is deprecated. ```ruby pod 'NFCPassportReader', git:'https://github.com/AndyQ/NFCPassportReader.git' ``` -------------------------------- ### getChallenge() Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the GET CHALLENGE command to receive an 8-byte nonce from the chip, which is used for BAC authentication. ```APIDOC ## getChallenge() ### Description Sends GET CHALLENGE command to receive 8-byte nonce from chip. Used by BAC authentication. ### Method `func getChallenge() async throws -> ResponseAPDU` ### APDU CLA=0x00, INS=0x84, P1/P2=0x00, Le=8 ### Response #### Success Response - **ResponseAPDU** - 8-byte random nonce from chip ``` -------------------------------- ### Install with CocoaPods (Deprecated) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/README.md Install the NFCPassportReader library using CocoaPods. Note that this method is deprecated. ```ruby pod 'NFCPassportReader', git: 'https://github.com/AndyQ/NFCPassportReader.git' ``` -------------------------------- ### Install NFCPassportReader via CocoaPods Source: https://github.com/andyq/nfcpassportreader/blob/main/readme.md Use this snippet to install the library using CocoaPods. Ensure you do not use Bitcode. ```ruby use_frameworks! pod 'NFCPassportReader', git:'https://github.com/AndyQ/NFCPassportReader.git' ``` -------------------------------- ### Typical Passport Reading Flow Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md Demonstrates the complete process of reading passport data using async/await. Includes reader creation, delegate setup for progress monitoring, reading passport details, accessing data, and verifying integrity. ```swift import NFCPassportReader // 1. Create reader let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: "pem") let reader = PassportReader(masterListURL: masterListURL) // 2. Optionally monitor progress class MyTracking: PassportReaderTrackingDelegate { func bacSucceeded() { print("✓ Authenticated") } func paceSucceeded() { print("✓ PACE successful") } } reader.trackingDelegate = MyTracking() do { // 3. Read passport (async/await) let passport = try await reader.readPassport( mrzKey: "12345678898012772508315", // From passport MRZ tags: [.DG1, .DG2, .DG7, .DG11], // Which datagroups to read skipSecureElements: true ) // 4. Access data print("Name: \(passport.firstName) \(passport.lastName)") if let image = passport.passportImage { imageView.image = image } // 5. Check verification (automatic if masterListURL was set) if passport.passportDataNotTampered { print("✓ Data integrity verified") } } catch let error as NFCPassportReaderError { print("Error: \(error.value)") } ``` -------------------------------- ### Implement PassportReaderTrackingDelegate Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/types.md Example of how to implement the PassportReaderTrackingDelegate protocol to receive callbacks for PACE and BAC authentication success. Assign an instance of this class to the reader's trackingDelegate property. ```swift class PassportTracking: PassportReaderTrackingDelegate { func paceSucceeded() { print("✓ PACE authentication successful") } func bacSucceeded() { print("✓ BAC authentication successful") } } let tracker = PassportTracking() reader.trackingDelegate = tracker ``` -------------------------------- ### Custom Authentication Monitoring Example Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Implement the PassportReaderTrackingDelegate protocol to monitor authentication events. This allows for custom logging or actions based on the success or failure of PACE and BAC authentication. ```swift class MyTracker: PassportReaderTrackingDelegate { func paceSucceeded() { print("✓ PACE successful - using modern authentication") } func paceFailed() { print("⚠ PACE failed - falling back to BAC") } func bacSucceeded() { print("✓ BAC successful - session keys established") } } reader.trackingDelegate = MyTracker() let passport = try await reader.readPassport(mrzKey: mrzKey) ``` -------------------------------- ### Get Challenge for BAC Authentication Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the GET CHALLENGE command to receive an 8-byte nonce from the chip, essential for BAC authentication. ```swift func getChallenge() async throws -> ResponseAPDU ``` -------------------------------- ### Complete Passport Reading Example Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md This Swift code demonstrates how to set up a view controller to read NFC passport data. It includes initializing the PassportReader, handling user input for the MRZ key, initiating the passport reading process, updating the UI with passport information, and displaying any verification errors. ```swift import UIKit import NFCPassportReader class PassportViewController: UIViewController { @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var documentLabel: UILabel! let reader = PassportReader( masterListURL: Bundle.main.url(forResource: "masterList", withExtension: "pem") ) @IBAction func readPassportTapped(_ sender: UIButton) { Task { await readPassport() } } func readPassport() async { let mrzKey = getUserInput() // From text field do { let passport = try await reader.readPassport( mrzKey: mrzKey, tags: [.DG1, .DG2, .DG7], skipSecureElements: true ) updateUI(with: passport) if !passport.verificationErrors.isEmpty { showWarning("Verification incomplete: \(passport.verificationErrors.count) error(s)") } } catch let error as NFCPassportReaderError { showError("Reading failed: \(error.value)") } } func updateUI(with passport: NFCPassportModel) { nameLabel.text = "\(passport.firstName) \(passport.lastName)" documentLabel.text = "Document: \(passport.documentNumber)" imageView.image = passport.passportImage } } ``` -------------------------------- ### Install via Swift Package Manager Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md Add the NFCPassportReader library to your Xcode project using Swift Package Manager. Select the main branch for async/await support. ```swift https://github.com/AndyQ/NFCPassportReader.git ``` -------------------------------- ### Example Application Flow for Scanning Passport Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/README.md This Swift code demonstrates the flow for scanning a passport using the NFCPassportReader. It includes creating a reader, reading passport data, and handling potential errors. ```swift import NFCPassportReader import UIKit class ViewController: UIViewController { func scanPassport() { Task { do { // Create reader with optional passive authentication let masterList = Bundle.main.url(forResource: "masterList", withExtension: "pem") let reader = PassportReader(masterListURL: masterList) // Read passport let passport = try await reader.readPassport( mrzKey: "12345678898012772508315", // From UI input tags: [.DG1, .DG2, .DG11], skipSecureElements: true ) // Display results DispatchQueue.main.async { self.nameLabel.text = "\(passport.firstName) \(passport.lastName)" self.photoImageView.image = passport.passportImage if passport.passportDataNotTampered { self.statusLabel.text = "✓ Verified" } else { self.statusLabel.text = "⚠ Verification failed" for error in passport.verificationErrors { print("Error: \(error)") } } } } catch let error as NFCPassportReaderError { DispatchQueue.main.async { self.showError(error.value) } } } } } ``` -------------------------------- ### Accessing Face Image Information Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/types.md Example demonstrating how to access and print the eye color and hair color from a passport's face image information. This code safely unwraps optional properties. ```swift if let faceInfo = passport.faceImageInfo { if let eyeColor = faceInfo.eyeColor { print("Eye color: \(eyeColor)") } if let hairColor = faceInfo.hairColor { print("Hair color: \(hairColor)") } } ``` -------------------------------- ### Get Hashes for Datagroups Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passive-authentication.md Computes and retrieves hashes for specified datagroups using a given hash algorithm. Useful for manual hash comparison. ```swift let hashes = passport.getHashesForDatagroups(hashAlgorythm: "SHA256") for (dgId, hashBytes) in hashes { let hex = binToHexRep(hashBytes) print("\(dgId.getName()): \(hex)") } ``` -------------------------------- ### Get Challenge APDU Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Constructs an APDU command to request a nonce (challenge) from the passport. This is often used in authentication protocols. ```swift // Request 8-byte nonce let cmd = NFCISO7816APDU( instructionClass: 0x00, instructionCode: 0x84, // GET CHALLENGE p1Parameter: 0x00, p2Parameter: 0x00, data: Data(), expectedResponseLength: 8 ) ``` -------------------------------- ### Perform BAC and Get Session Keys Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Executes the BAC authentication protocol and establishes secure messaging session keys. Requires an MRZ key formatted as ''. ```swift public func performBACAndGetSessionKeys(mrzKey: String) async throws ``` -------------------------------- ### MRZ Key Format Example Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/errors.md The MRZ key is a string derived from passport details. Ensure it follows the specified format: ``. ```plaintext ``` ```plaintext 12345678898012772508315 ``` -------------------------------- ### Ensuring SOD Inclusion for Verification Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passive-authentication.md When reading passport data, ensure the Statement of Data (SOD) is included in the read data groups for proper verification. This example shows the correct way to include SOD. ```swift // Correct: SOD automatically included let passport = try await reader.readPassport(mrzKey: key, tags: [.DG1, .DG2]) // Avoid: Doesn't include SOD let passport = try await reader.readPassport(mrzKey: key, tags: [.DG1]) ``` -------------------------------- ### sendMSEKAT(keyData:idData:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the Manage Security Environment: Set Authentication Template command, used for Chip Authentication in DES mode. Requires key data and optionally accepts key ID data. ```APIDOC ## sendMSEKAT(keyData:idData:) ### Description Sends Manage Security Environment: Set Authentication Template command (for Chip Authentication in DES mode). ### Method `func sendMSEKAT(keyData: Data, idData: Data?) async throws -> ResponseAPDU` ### Parameters #### Path Parameters - **keyData** (Data) - Required - Key data object (tag 0x91) with public key - **idData** (Data?) - Optional - Key ID data object (tag 0x84) ### APDU CLA=0x00, INS=0x22, P1=0x41, P2=0xA6 ### Response #### Success Response - **ResponseAPDU** - Response from the command ``` -------------------------------- ### Create PassportReader Instance Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md Instantiate the PassportReader class to begin reading passport data. ```swift let reader = PassportReader() ``` -------------------------------- ### Initialize PassportReader Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passport-reader.md Create a new PassportReader instance. Optionally configure it for passive authentication by providing a URL to a PEM-formatted master list file for CSCA certificate validation. Leave masterListURL as nil to skip verification. ```swift let reader = PassportReader() ``` ```swift if let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: "pem") { let reader = PassportReader(masterListURL: masterListURL) } ``` -------------------------------- ### NFCPassportReaderError Handling Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passport-reader.md Details the error types that can be thrown by the PassportReader and provides an example of how to catch and handle them. ```APIDOC ## Error Handling All errors throw `NFCPassportReaderError`. Check the `value` property for human-readable messages: ```swift do { let passport = try await reader.readPassport(mrzKey: key, tags: [.DG1, .DG2]) } catch let error as NFCPassportReaderError { switch error { case .InvalidMRZKey: print("Check passport number and dates") case .ConnectionError: print("Device moved away from NFC chip") case .UserCanceled: print("User dismissed NFC dialog") default: print("Error: \(error.value)") } } ``` ``` -------------------------------- ### Initialize Passport Reader Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md Create an instance of PassportReader. Use the minimal initializer for basic use or provide a master list URL for passive authentication. ```swift // Minimal let reader = PassportReader() ``` ```swift // With passive authentication let masterList = Bundle.main.url(forResource: "masterList", withExtension: "pem") let reader = PassportReader(masterListURL: masterList) ``` -------------------------------- ### Send MSE:Set Authentication Template Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the Manage Security Environment: Set Authentication Template command, used for Chip Authentication in DES mode. Requires key data and optionally key ID data. ```swift func sendMSEKAT(keyData: Data, idData: Data?) async throws -> ResponseAPDU ``` -------------------------------- ### Customize NFC Messages Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md Provide custom text to display on the user's device during NFC interactions. This helps guide the user through the passport reading process. ```swift let customMessages: (NFCViewDisplayMessage) -> String? = { msg in switch msg { case .requestPresentPassport: return "Hold iPhone near your passport" case .authenticatingWithPassport: return "Reading your passport..." case .readingDataGroupProgress(let dg, let progress): return "Reading \(dg.getName()): \(progress)%" default: return nil // Use default message } } let passport = try await reader.readPassport( mrzKey: mrzKey, tags: [.DG1, .DG2], customDisplayMessage: customMessages ) ``` -------------------------------- ### PassportReader Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passport-reader.md Creates a new PassportReader instance. Optionally configures passive authentication using a master list URL. ```APIDOC ## init(masterListURL:) ### Description Creates a new PassportReader instance with optional passive authentication configuration. ### Method `init` ### Parameters #### Path Parameters - **masterListURL** (URL?) - Optional - URL to PEM-formatted master list file for CSCA certificate validation during passive authentication. Leave nil to skip verification. ### Request Example ```swift // Create reader without passive authentication let reader = PassportReader() // Create reader with passive authentication enabled if let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: "pem") { let reader = PassportReader(masterListURL: masterListURL) } ``` ``` -------------------------------- ### Select File and Read Contents Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Internal method to select a file by its ID and read its entire contents. Useful for accessing specific data files on the chip. ```swift func selectFileAndRead(tag: [UInt8]) async throws -> [UInt8] ``` -------------------------------- ### Restore Passport Model from Exported Data Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/nfc-passport-model.md Example demonstrating how to reconstruct an NFCPassportModel from previously exported data. The `exportedData` variable is assumed to be a dictionary containing base64-encoded data groups. ```swift let dump = exportedData // From dumpPassportData() let restoredPassport = NFCPassportModel(from: dump) ``` -------------------------------- ### Accessing DG2 (Facial Image) Data Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/data-groups.md Retrieves and displays the official passport photograph from the DG2 data group. It demonstrates how to get a UIImage for display and access associated facial metadata. ```swift if let dg2 = passport.getDataGroup(.DG2) as? DataGroup2, let image = dg2.getImage() { imageView.image = image // Display photo } // Check image metadata if let faceInfo = passport.faceImageInfo { if let eyeColor = faceInfo.eyeColor { print("Eyes: \(eyeColor)") } } ``` -------------------------------- ### Calling readPassport Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md Demonstrates how to call the `readPassport` function using `await` within a Swift context. Ensure the calling context is also asynchronous. ```swift let passport = try await reader.readPassport(mrzKey: key) ``` -------------------------------- ### TagReader Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Initializes the TagReader by wrapping the CoreNFC ISO 7816 tag interface. ```swift init(tag: NFCISO7816Tag) ``` -------------------------------- ### Customizing NFC Display Messages Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/types.md Example of how to provide a customizer closure to override default display messages for the NFC UI alert box. This allows for localized or specific user prompts. ```swift let customizer: (NFCViewDisplayMessage) -> String? = { msg in switch msg { case .requestPresentPassport: return "Hold iPhone near your passport chip" case .authenticatingWithPassport: return "Reading passport..." default: return nil // Use default message } } let passport = try await reader.readPassport(mrzKey: key, customDisplayMessage: customizer) ``` -------------------------------- ### SecureMessaging Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Initializes SecureMessaging with encryption algorithm, session keys, and send sequence counter. The encryption algorithm defaults to DES. ```swift public init( encryptionAlgorithm: SecureMessagingSupportedAlgorithms = .DES, ksenc: [UInt8], ksmac: [UInt8], ssc: [UInt8] ) ``` -------------------------------- ### SecureMessaging Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Initializes the SecureMessaging class with session keys and an optional encryption algorithm. This is used to establish a secure channel for APDU communication after initial authentication. ```APIDOC ## init(encryptionAlgorithm:ksenc:ksmac:ssc:) ### Description Initializes the SecureMessaging class with session keys and an optional encryption algorithm. This is used to establish a secure channel for APDU communication after initial authentication. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **encryptionAlgorithm** (SecureMessagingSupportedAlgorithms) - Optional - DES (3DES) or AES encryption. Defaults to .DES. - **ksenc** ([UInt8]) - Required - Session encryption key from BAC/PACE/CA. - **ksmac** ([UInt8]) - Required - Session MAC key from BAC/PACE/CA. - **ssc** ([UInt8]) - Required - Send sequence counter, usually [0,0,0,0,0,0,0,1]. ``` -------------------------------- ### doInternalAuthentication(challenge:useExtendedMode:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the INTERNAL AUTHENTICATE command for Active Authentication, where the chip signs the provided challenge with its private key. Optionally supports extended mode for larger responses. ```APIDOC ## doInternalAuthentication(challenge:useExtendedMode:) ### Description Sends INTERNAL AUTHENTICATE command for Active Authentication. Chip signs the challenge with its private key. ### Method `func doInternalAuthentication(challenge: [UInt8], useExtendedMode: Bool) async throws -> ResponseAPDU` ### Parameters #### Path Parameters - **challenge** ([UInt8]) - Required - 8-byte nonce to be signed by chip - **useExtendedMode** (Bool) - Optional - If true, expect large responses (up to 65535 bytes) ### APDU CLA=0x00, INS=0x88, P1/P2=0x00 ### Response #### Success Response - **ResponseAPDU** - Signed challenge (signature length varies by key type) ``` -------------------------------- ### Enable Passport Verification with Master List Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/README.md Initialize PassportReader with a master list URL for verification. Reads passport data and checks for correct signing and data integrity. ```swift let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: "pem")! let reader = PassportReader(masterListURL: masterListURL) let passport = try await reader.readPassport(mrzKey: mrzKey, tags: [.DG1, .DG2]) if passport.passportCorrectlySigned && passport.passportDataNotTampered { print("✓ Passport verified") } ``` -------------------------------- ### Initialize PassportReader with Master List Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passive-authentication.md Initialize the PassportReader with the URL to your master list of CSCA certificates. This is required for verifying the authenticity of the passport's Security Object Document (SOD). ```swift let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: "pem")! let reader = PassportReader(masterListURL: masterListURL) ``` -------------------------------- ### Combine CSCA Certificates into Master List Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passive-authentication.md Combine individual country CSCA certificates into a single PEM-formatted master list file. Ensure certificates are in PEM format; convert from DER if necessary. ```bash # Extract single country's CSCA cert from ICAO PKD curl -o csca-US.pem "https://icao.pkd.example/certificate" # Combine multiple certs into one master list cat csca-US.pem csca-DE.pem csca-FR.pem > masterList.pem # Convert DER to PEM if needed openssl x509 -inform DER -in csca.der -out csca.pem ``` -------------------------------- ### Compute Hashes for Data Groups Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/data-groups.md Demonstrates computing hashes for all data groups within a passport using a specified algorithm. The output provides a mapping of data group IDs to their hexadecimal hash representations, useful for verification purposes. ```swift let hashes = passport.getHashesForDatagroups(hashAlgorythm: "SHA256") for (dgId, hashBytes) in hashes { print("\(dgId.getName()): \(binToHexRep(hashBytes))") } ``` -------------------------------- ### Initialize PassportReader and Read Passport Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/00-START-HERE.md This snippet shows how to initialize the PassportReader and initiate the passport reading process with a provided MRZ key. Ensure you have imported the NFCPassportReader library. ```swift import NFCPassportReader let reader = PassportReader() let passport = try await reader.readPassport(mrzKey: "12345678898012772508315") ``` -------------------------------- ### selectFileAndRead(tag:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Internal method to select a file by its ID and read its entire contents. This is a helper method not intended for direct external use. ```APIDOC ## selectFileAndRead(tag:) ### Description Internal method: SELECT file by ID and read entire file contents. ### Method `func selectFileAndRead(tag: [UInt8]) async throws -> [UInt8]` ### Parameters #### Path Parameters - **tag** ([UInt8]) - Required - File ID bytes (e.g., [0x01, 0x01] for DG1) ### Response #### Success Response - **[UInt8]** - Complete file data ``` -------------------------------- ### PACEHandler Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Initializes a PACE handler, which requires CardAccess data and an NFC tag reader. Throws an error if PACE is not supported by the chip. ```swift public init(cardAccess: CardAccess, tagReader: TagReader) throws ``` -------------------------------- ### verifyActiveAuthentication(challenge:signature:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/nfc-passport-model.md Verifies an active authentication challenge/response pair using the public key stored in DG15. This method is crucial for confirming the authenticity of the chip. ```APIDOC ## verifyActiveAuthentication(challenge:signature:) ### Description Verifies an active authentication challenge/response pair using the public key in DG15. ### Method Swift Function ### Parameters #### Path Parameters - **challenge** ([UInt8]) - Required - 8-byte challenge sent to the chip - **signature** ([UInt8]) - Required - Signature returned by the chip's internal authentication command ### Request Example ```swift let challenge: [UInt8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] let signature = try await tagReader.doInternalAuthentication(challenge: challenge) passport.verifyActiveAuthentication(challenge: challenge, signature: signature.data) if passport.activeAuthenticationPassed { print("Chip authentication successful - chip is genuine") } ``` ``` -------------------------------- ### Passive Authentication Options Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md Choose how to perform passive authentication. It can be automatic via initialization, manual after setting the master list, or for data integrity only by skipping certificate checks. ```swift // Option 1: Via initialization let reader = PassportReader(masterListURL: masterListURL) // Automatic verification during/after read ``` ```swift // Option 2: Manual reader.setMasterListURL(masterListURL) passport.verifyPassport(masterListURL: masterListURL) ``` ```swift // Option 3: Data integrity only passport.verifyPassport(masterListURL: nil) // No certificate check, hash-only ``` -------------------------------- ### performBACAndGetSessionKeys Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Executes the complete BAC authentication protocol and establishes secure messaging session keys. This method derives encryption and MAC keys from the MRZ, authenticates with the chip, and sets up secure messaging. ```APIDOC ## performBACAndGetSessionKeys ### Description Executes the complete BAC authentication protocol and establishes secure messaging session keys. ### Method ```swift public func performBACAndGetSessionKeys(mrzKey: String) async throws ``` ### Parameters #### Path Parameters - **mrzKey** (String) - Required - MRZ key formatted as `` ### Throws - `NFCPassportReaderError.NoConnectedTag` — No active NFC connection - `NFCPassportReaderError.InvalidMRZKey` — BAC authentication failed (empty response) ``` -------------------------------- ### ChipAuthenticationHandler Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Initializes the ChipAuthenticationHandler. Called internally when DG14 is read and contains Chip Authentication public keys. Requires the DG14 data group and an NFC tag reader. ```swift init(dg14: DataGroup14, tagReader: TagReader) ``` -------------------------------- ### doMutualAuthentication(cmdData:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the MUTUAL AUTHENTICATE command as part of BAC. The cmdData contains the encrypted challenge and host nonce. Returns an encrypted response containing decrypted nonce and session keys. ```APIDOC ## doMutualAuthentication(cmdData:) ### Description Sends MUTUAL AUTHENTICATE command as part of BAC. The cmdData contains encrypted challenge and host nonce. ### Method `func doMutualAuthentication(cmdData: Data) async throws -> ResponseAPDU` ### Parameters #### Path Parameters - **cmdData** (Data) - Required - Encrypted challenge and nonce from BACHandler ### APDU CLA=0x00, INS=0x82, P1/P2=0x00 ### Response #### Success Response - **ResponseAPDU** - Encrypted response containing decrypted nonce and session keys ### Throws - `NFCPassportReaderError.InvalidMRZKey` — Authentication failed (check MRZ) ``` -------------------------------- ### Initialize PassportReader and Read Passport Data Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md Instantiate the PassportReader and initiate reading specific data groups from an NFC passport. Ensure the MRZ key and desired data groups are provided. ```swift import NFCPassportReader let reader = PassportReader(masterListURL: nil) let passport = try await reader.readPassport(mrzKey: mrzKey, tags: [.DG1, .DG2]) ``` -------------------------------- ### Create and Read Passport with NFCPassportReader Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/README.md Instantiate PassportReader and read passport data, specifying desired data groups. Requires MRZ key and imports NFCPassportReader. ```swift import NFCPassportReader let reader = PassportReader() let passport = try await reader.readPassport( mrzKey: "12345678898012772508315", tags: [.DG1, .DG2, .DG7] ) print("Name: \(passport.firstName) \(passport.lastName)") if let image = passport.passportImage { display(image) } ``` -------------------------------- ### Source Code Structure Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md The library is organized into several directories, each containing specific components. Key areas include the main public class, data models, authentication handlers, data group implementations, cryptography utilities, and NFC-specific components. ```tree Sources/NFCPassportReader/ ├── PassportReader.swift # Main public class ├── NFCPassportModel.swift # Passport data model ├── Errors.swift # Error enumerations ├── NFCViewDisplayMessage.swift # UI message types ├── AuthenticationHandlers/ │ ├── BACHandler.swift │ ├── PACEHandler.swift │ └── ChipAuthenticationHandler.swift ├── DataGroups/ # DataGroup implementations │ ├── DataGroup.swift # Base class │ ├── DataGroup1.swift │ ├── DataGroup2.swift │ ├── DataGroup7.swift │ ├── DataGroup11.swift │ ├── DataGroup12.swift │ ├── DataGroup14.swift │ ├── DataGroup15.swift │ ├── SOD.swift │ └── DataGroupId.swift ├── Models/ │ └── FaceImageInfo.swift ├── Cryptography/ │ ├── SecureMessaging.swift │ ├── SecureMessagingSessionKeyGenerator.swift │ ├── AES_3DES_DESEncryption.swift │ ├── OpenSSLUtils.swift │ └── X509Wrapper.swift ├── NFC/ │ ├── TagReader.swift │ └── ResponseAPDU.swift ├── Utilities/ │ ├── Utils.swift │ ├── DataGroupParser.swift │ └── Logging.swift ``` -------------------------------- ### Enable Debug Logging for PassportReader Source: https://github.com/andyq/nfcpassportreader/blob/main/readme.md Instantiate `PassportReader` with a specific log level, such as `.debug`, to enable verbose logging output to the console. ```swift let reader = PassportReader(logLevel: .debug) ``` -------------------------------- ### Provide Custom Active Authentication Challenge Source: https://github.com/andyq/nfcpassportreader/blob/main/readme.md Supply a custom Active Authentication challenge to ensure the challenge/response occurred within the current session and prevent replay attacks. The signature can then be sent to a backend for validation. ```swift let customChallenge = Data() passportReader.readPassport(mrzKey: mrzKey, tags: [.COM, .DG1, .DG2], activeAuthenticationChallenge: customChallenge, completed: { (error) in ... }) ``` -------------------------------- ### Secure Messaging with TagReader Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Demonstrates how TagReader automatically handles secure messaging after BAC/PACE authentication. It encrypts commands, computes MAC, and verifies responses. ```swift // Before BAC: let cmd = NFCISO7816APDU( instructionClass: 0x00, instructionCode: 0xB0, p1Parameter: 0x00, p2Parameter: 0x00, data: Data(), expectedResponseLength: 0xA0 ) let response = try await tagReader.send(cmd: cmd) // CMD sent in plaintext // After BAC (tagReader.secureMessaging is set): // SecureMessaging automatically: // 1. Increments SSC // 2. Encrypts command data // 3. Computes MAC // 4. Sends protected APDU // 5. Verifies response MAC // 6. Decrypts response // Application sees unencrypted response ``` -------------------------------- ### Read Passport Data with Async/Await Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passport-reader.md Initiates a passport reading session using async/await. Specify data groups, authentication challenges, and security options. Use this for retrieving complete passport data. ```swift @available(iOS 15, *) public func readPassport( mrzKey: String, tags: [DataGroupId] = [], aaChallenge: [UInt8]? = nil, skipSecureElements: Bool = true, skipCA: Bool = false, skipPACE: Bool = false, useExtendedMode: Bool = false, customDisplayMessage: ((NFCViewDisplayMessage) -> String?)? = nil ) async throws -> NFCPassportModel ``` ```swift let mrzKey = "12345678898012772508315" do { let passport = try await reader.readPassport( mrzKey: mrzKey, tags: [.DG1, .DG2, .DG7], skipSecureElements: true, skipCA: false ) print("Passport number: \(passport.documentNumber)") print("Full name: \(passport.firstName) \(passport.lastName)") if let image = passport.passportImage { // Display passport photo } } catch let error as NFCPassportReaderError { print("Reading failed: \(error.value)") } ``` -------------------------------- ### Verify Active Authentication Challenge-Response Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/nfc-passport-model.md Verifies an active authentication challenge-response pair using the public key found in DG15. This confirms the chip's authenticity. ```swift let challenge: [UInt8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] let signature = try await tagReader.doInternalAuthentication(challenge: challenge) passport.verifyActiveAuthentication(challenge: challenge, signature: signature.data) if passport.activeAuthenticationPassed { print("Chip authentication successful - chip is genuine") } ``` -------------------------------- ### Initialize NFCPassportModel from Dump Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/nfc-passport-model.md Reconstructs an NFCPassportModel from a dictionary representing an exported data dump. The dictionary should map data group names to their base64-encoded string representations. ```swift public init(from dump: [String:String]) ``` -------------------------------- ### Read Binary APDU Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Constructs an APDU command to read binary data. Specify the expected response length to control how much data is read. ```swift // Read 0xA0 bytes from current offset let cmd = NFCISO7816APDU( instructionClass: 0x00, instructionCode: 0xB0, // READ BINARY p1Parameter: 0x00, // Offset high byte p2Parameter: 0x00, // Offset low byte data: Data(), expectedResponseLength: 0xA0 ) ``` -------------------------------- ### doPACE(mrzKey:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Executes the PACE authentication protocol using the provided MRZ key. This method handles the entire PACE authentication process, including key generation, establishment of a secure channel, and mutual authentication. ```APIDOC ## doPACE(mrzKey:) ### Description Executes the PACE authentication protocol using the MRZ. ### Method public func doPACE ### Parameters #### Path Parameters - **mrzKey** (String) - Yes - MRZ key ### Protocol Steps 1. Reads and verifies chip nonce using MSE:Set AT command 2. Generates ephemeral key pair for key agreement 3. Sends public key to chip and receives chip's public key 4. Establishes shared secret via ECDH or DH key agreement 5. Derives encryption and MAC keys from shared secret 6. Verifies mutual authentication ### Throws - `NFCPassportReaderError.NotYetSupported` — PACE not supported - `NFCPassportReaderError.PACEError` — Protocol failure with step and reason ### Supported Algorithms - Mapping types: Generic Mapping (GM) only. CAM and IM not yet supported. - Key agreement: DH (Diffie-Hellman) and ECDH (Elliptic Curve DH) - Ciphers: DESede (3DES) and AES - Hash algorithms: SHA-1 and SHA-256 ``` -------------------------------- ### Internal Authentication for Active Authentication Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Sends the INTERNAL AUTHENTICATE command for Active Authentication. The chip signs the provided challenge with its private key. ```swift func doInternalAuthentication( challenge: [UInt8], useExtendedMode: Bool ) async throws -> ResponseAPDU ``` -------------------------------- ### BACHandler Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Initializes a BAC handler with a specific NFC tag reader. ```swift public init(tagReader: TagReader) ``` -------------------------------- ### Read All Available Data Groups Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/data-groups.md Shows how to read all available data groups from a passport by passing an empty array for tags. This reads all data groups present in the COM (Card Object Model). ```swift let passport = try await reader.readPassport( mrzKey: mrzKey, tags: [] // Empty = read all in COM ) ``` -------------------------------- ### Async Context for Passport Reading Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/api-overview.md Shows how to use `readPassport` within a `Task` block for handling asynchronous operations and errors. This is useful for updating UI or performing other tasks after reading. ```swift Task { do { let passport = try await reader.readPassport(mrzKey: key) updateUI(passport) } catch { showError(error) } } ``` -------------------------------- ### Accessing COM Data Group Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/data-groups.md Retrieves and prints the LDS version and a list of available data groups from the COM data group. This is automatically read first to discover chip contents. ```swift if let com = passport.getDataGroup(.COM) as? COM { print("LDS version: \(com.version)") print("Available groups: \(com.dataGroupsPresent.joined(separator: ", "))") } ``` -------------------------------- ### Execute PACE Authentication Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Executes the PACE authentication protocol using the provided MRZ key. This method involves multiple steps including key generation, agreement, and mutual authentication. It supports various cryptographic algorithms and may throw errors if PACE is not supported or if the protocol fails. ```swift public func doPACE(mrzKey: String) async throws ``` -------------------------------- ### Select DG1 File APDU Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Constructs an APDU command to select the DG1 file. Use this to initiate reading specific file data from the passport. ```swift // Select DG1 file let cmd = NFCISO7816APDU( instructionClass: 0x00, instructionCode: 0xA4, // SELECT FILE p1Parameter: 0x02, // By file ID p2Parameter: 0x0C, // Expecting response data: Data([0x01, 0x01]), // DG1 file ID expectedResponseLength: 256 ) ``` -------------------------------- ### verifyPassport(masterListURL:useCMSVerification:) Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/nfc-passport-model.md Performs passive authentication by verifying the SOD signature against a master list and validating data integrity. Optionally uses OpenSSL CMS verification. ```APIDOC ## verifyPassport(masterListURL:useCMSVerification:) ### Description Performs passive authentication: verifies SOD signature against master list and validates data integrity. ### Method Swift Function ### Parameters #### Path Parameters - **masterListURL** (URL?) - Optional - PEM file with CSCA certificates. If nil, only data tampering check is performed. - **useCMSVerification** (Bool) - Optional - Use OpenSSL CMS verification for SOD signature. Default uses RFC5652 manual verification. Defaults to false. ### Request Example ```swift let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: "pem") passport.verifyPassport(masterListURL: masterListURL) if passport.passportCorrectlySigned { print("Passport authenticated by trusted authority") } if passport.passportDataNotTampered { print("All data group hashes match SOD") } ``` ``` -------------------------------- ### Export Passport Data to File Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md Save selected passport data groups to a JSON file for later retrieval. Ensure the file URL is valid before writing. ```swift // Export selected groups let dump = passport.dumpPassportData( selectedDataGroups: [.DG1, .DG2], includeActiveAuthenticationData: true ) // Save to file if let jsonData = try? JSONSerialization.data(withJSONObject: dump) { try? jsonData.write(to: fileURL) } // Later: restore from file if let jsonData = try? Data(contentsOf: fileURL), let dump = try? JSONSerialization.jsonObject(as: [String: String].self, from: jsonData) { let restoredPassport = NFCPassportModel(from: dump) } ``` -------------------------------- ### BACHandler Initialization Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/authentication-handlers.md Initializes a BACHandler with a specific NFC tag reader. This handler is responsible for performing BAC authentication and establishing secure messaging. ```APIDOC ## BACHandler Initialization ### Description Creates a BAC handler bound to a specific NFC tag reader. ### Method ```swift public init(tagReader: TagReader) ``` ### Parameters #### Path Parameters - **tagReader** (TagReader) - Required - The NFC tag reader for sending APDU commands ``` -------------------------------- ### Import NFCPassportReader Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/quick-start.md Import the NFCPassportReader library into your Swift file to use its functionalities. ```swift import NFCPassportReader ``` -------------------------------- ### SourceType Enumeration Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/types.md Defines the source device used to capture the facial image, distinguishing between static photos and video frames from various digital or analog cameras. ```swift public enum SourceType: Int { case unspecified = 0x00 case staticPhotoUnknownSource = 0x01 case staticPhotoDigitalCam = 0x02 case staticPhotoScanner = 0x03 case videoFrameUnknownSource = 0x04 case videoFrameAnalogCam = 0x05 case videoFrameDigitalCam = 0x06 case unknown = 0x07 } ``` -------------------------------- ### Enable OpenSSL CMS Verification Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/passive-authentication.md Configures PassportReader to use OpenSSL CMS for SOD signature verification instead of the default RFC5652 method. Use this when RFC5652 verification fails due to specific hash algorithm combinations. ```swift // Use OpenSSL CMS verification instead of default RFC5652 reader.passiveAuthenticationUsesOpenSSL = true passport.verifyPassport(masterListURL: masterListURL, useCMSVerification: true) ``` -------------------------------- ### Internal Authenticate APDU Source: https://github.com/andyq/nfcpassportreader/blob/main/_autodocs/tag-reader-and-apdu.md Constructs an APDU command for internal authentication, typically used after receiving a challenge. The response contains a signature. ```swift // Active authentication challenge let cmd = NFCISO7816APDU( instructionClass: 0x00, instructionCode: 0x88, // INTERNAL AUTHENTICATE p1Parameter: 0x00, p2Parameter: 0x00, data: Data(challenge), // 8-byte challenge expectedResponseLength: 256 // Signature response ) ```