### Reading MRTD Data via NFC with PACE/BAC Session Source: https://github.com/zeropass/dmrtd/blob/master/README.md This example demonstrates connecting to an NFC-enabled passport using the DMRTD library, establishing a secure session via PACE or BAC, reading key elementary files like EF.COM, EF.DG1, EF.DG2, EF.DG14, EF.DG15, and EF.SOD, and performing active authentication. It requires the dmrtd package dependency and an NFC provider for Flutter apps, with inputs like access keys and MRDT data; outputs include parsed data groups and signatures. Limitations include incomplete parsing for some files and dependency on NFC hardware availability. ```dart import 'package:dmrtd/dmrtd.dart'; try { final nfc = NfcProvider(); await nfc.connect(iosAlertMessage: "Hold your iPhone near Passport"); final passport = Passport(nfc); nfc.setIosAlertMessage("Reading EF.CardAccess ..."); final cardAccess = await passport.readEfCardAccess(); _nfc.setIosAlertMessage("Initiating session with PACE or BAC..."); //set MrtdData mrtdData.isPACE = true; //initialize with PACE(set false if you want to do with DBA) mrtdData.isDBA = accessKey.PACE_REF_KEY_TAG == 0x01 ? true : false; if (isPace) { //PACE session await passport.startSessionPACE(accessKey, mrtdData.cardAccess!); } else { //BAC session await passport.startSession(accessKey as DBAKey); } nfc.setIosAlertMessage(formatProgressMsg("Reading EF.COM ...", 0)); final efcom = await passport.readEfCOM(); nfc.setIosAlertMessage(formatProgressMsg("Reading Data Groups ...", 20)); EfDG1 dg1; if (efcom.dgTags.contains(EfDG1.TAG)) { dg1 = await passport.readEfDG1(); } EfDG2 dg2; if (efcom.dgTags.contains(EfDG2.TAG)) { dg2 = await passport.readEfDG2(); } EfDG14 dg14; if (efcom.dgTags.contains(EfDG14.TAG)) { dg14 = await passport.readEfDG14(); } EfDG15 dg15; Uint8List sig; if (efcom.dgTags.contains(EfDG15.TAG)) { dg15 = await passport.readEfDG15(); nfc.setIosAlertMessage(formatProgressMsg("Doing AA ...", 60)); sig = await passport.activeAuthenticate(Uint8List(8)); } nfc.setIosAlertMessage(formatProgressMsg("Reading EF.SOD ...", 80)); final sod = await passport.readEfSOD(); } on Exception catch(e) { final se = e.toString().toLowerCase(); String alertMsg = "An error has occurred while reading Passport!"; if (e is PassportError) { if (se.contains("security status not satisfied")) { alertMsg = "Failed to initiate session with passport.\nCheck input data!"; } } if (se.contains('timeout')){ alertMsg = "Timeout while waiting for Passport tag"; } else if (se.contains("tag was lost")) { alertMsg = "Tag was lost. Please try again!"; } else if (se.contains("invalidated by user")) { alertMsg = ""; } errorAlertMsg = alertMsg; } finally { if (errorAlertMsg?.isNotEmpty) { await _nfc.disconnect(iosErrorMessage: errorAlertMsg); if (!Platform.isIOS) { // Show error to the user } } else { await _nfc.disconnect(iosAlertMessage: formatProgressMsg("Finished", 100)); } } ``` -------------------------------- ### Parse and validate MRZ data in Dart Source: https://context7.com/zeropass/dmrtd/llms.txt Implements MRZ parsing from text lines, checksum verification, and DBA key generation. Handles TD3 format (2 lines, 44 chars each) commonly found in passports. Includes example usage with validation. ```dart import 'package:dmrtd/dmrtd.dart'; import 'dart:typed_data'; import 'dart:convert'; class MRZValidator { // Parse MRZ from scanned text (TD3 format - 2 lines, 44 chars each) static MRZ parseMRZFromText(String line1, String line2) { // Combine lines and convert to bytes final mrzString = line1 + line2; final mrzBytes = Uint8List.fromList(mrzString.codeUnits); // Parse MRZ final mrz = MRZ(mrzBytes); print("Parsed MRZ:"); print(" Document Type: ${mrz.documentCode}"); print(" Country: ${mrz.country}"); print(" Name: ${mrz.firstName} ${mrz.lastName}"); print(" Document Number: ${mrz.documentNumber}"); print(" Nationality: ${mrz.nationality}"); print(" Date of Birth: ${mrz.dateOfBirth}"); print(" Gender: ${mrz.gender}"); print(" Date of Expiry: ${mrz.dateOfExpiry}"); print(" Optional Data: ${mrz.optionalData}"); print(" MRZ Version: ${mrz.version}"); return mrz; } // Calculate and verify check digit static bool verifyCheckDigit(String data, int expectedCheckDigit) { final calculatedCheckDigit = MRZ.calculateCheckDigit(data); return calculatedCheckDigit == expectedCheckDigit; } // Create DBA keys from MRZ static DBAKey createKeysFromMRZ(MRZ mrz) { return DBAKey.fromMRZ(mrz); } // Example usage with real MRZ data static void example() { try { // TD3 format example (standard passport) final line1 = "P checkNfcAvailability() async { final status = await NfcProvider.nfcStatus; switch (status) { case NfcStatus.enabled: print("NFC is available and enabled"); return true; case NfcStatus.disabled: print("NFC is available but disabled - prompt user to enable"); return false; case NfcStatus.notSupported: print("NFC is not supported on this device"); return false; } } Future readPassportWithErrorHandling(DBAKey accessKey) async { String? errorMessage; try { // Check NFC availability first if (!await checkNfcAvailability()) { throw Exception("NFC not available"); } // Connect with custom timeout _nfc.timeout = Duration(seconds: 15); await _nfc.connect( timeout: _nfc.timeout, iosAlertMessage: "Hold your iPhone near Passport" ); final passport = Passport(_nfc); // Update iOS NFC dialog messages _nfc.setIosAlertMessage("Authenticating..."); await passport.startSession(accessKey); _nfc.setIosAlertMessage("Reading passport data..."); final efCom = await passport.readEfCOM(); final efDg1 = await passport.readEfDG1(); print("Successfully read passport for: ${efDg1.mrz.firstName} ${efDg1.mrz.lastName}"); } on PassportError catch (e) { if (e.message.contains("security status not satisfied")) { errorMessage = "Authentication failed. Please check passport details."; } else if (e.message.contains("file not found")) { errorMessage = "Required data not available on this passport."; } else { errorMessage = "Passport error: ${e.message}"; } } on NfcProviderError catch (e) { final errorStr = e.toString().toLowerCase(); if (errorStr.contains("timeout")) { errorMessage = "Connection timeout. Please hold passport steady."; } else if (errorStr.contains("tag was lost")) { errorMessage = "Connection lost. Please try again."; } else if (errorStr.contains("invalidated by user")) { errorMessage = ""; // User cancelled } else { errorMessage = "NFC error: $e"; } } catch (e) { errorMessage = "Unexpected error: $e"; } finally { // Always disconnect, show appropriate message if (_nfc.isConnected()) { if (errorMessage != null && errorMessage.isNotEmpty) { await _nfc.disconnect( iosErrorMessage: errorMessage ); if (!Platform.isIOS) { // Show error to user on Android print("Error: $errorMessage"); } } else { await _nfc.disconnect( iosAlertMessage: "Success!" ); } } } } } ``` -------------------------------- ### Passport Session Initialization with PACE - Dart Source: https://context7.com/zeropass/dmrtd/llms.txt Establishes a secure session using PACE protocol for enhanced security and compatibility with newer passports. Reads EF.CardAccess first, creates access key with PACE mode enabled, and initializes PACE session. Also demonstrates reading card security data after successful session establishment. Provides better security than BAC for newer passport implementations. ```dart import 'package:dmrtd/dmrtd.dart'; Future initializeWithPACE() async { final nfc = NfcProvider(); try { await nfc.connect( iosAlertMessage: "Hold your iPhone near Passport" ); final passport = Passport(nfc); // Read EF.CardAccess first (required for PACE) nfc.setIosAlertMessage("Reading card capabilities..."); final cardAccess = await passport.readEfCardAccess(); // Create access key with PACE mode enabled final accessKey = DBAKey( 'AB1234567', // Passport number DateTime(1985, 5, 15), // Date of birth DateTime(2025, 6, 30), // Date of expiry paceMode: true ); // Initialize PACE session nfc.setIosAlertMessage("Establishing secure connection..."); await passport.startSessionPACE(accessKey, cardAccess); print("PACE session established successfully"); // Read card security if needed final cardSecurity = await passport.readEfCardSecurity(); } catch (e) { print("PACE initialization failed: $e"); } finally { await nfc.disconnect(); } } ``` -------------------------------- ### Passport Session Initialization with BAC - Dart Source: https://context7.com/zeropass/dmrtd/llms.txt Establishes a secure session with a passport using Basic Access Control protocol based on MRZ data. Connects to NFC tag, creates passport instance, generates access keys from MRZ data, and initializes secure session using BAC protocol. Handles PassportError and ComProviderError exceptions for authentication failures and NFC communication issues. ```dart import 'package:dmrtd/dmrtd.dart'; Future initializePassportSession() async { try { // Connect to NFC tag final nfc = NfcProvider(); await nfc.connect( timeout: Duration(seconds: 10), iosAlertMessage: "Hold your iPhone near Passport" ); // Create passport instance final passport = Passport(nfc); // Create access keys from passport MRZ data final dbaKeys = DBAKey( 'L898902C3', // Passport number DateTime(1974, 8, 20), // Date of birth DateTime(2024, 12, 31) // Date of expiry ); // Establish secure session using BAC protocol await passport.startSession(dbaKeys); print("Secure session established successfully"); // Continue reading passport data... } on PassportError catch (e) { if (e.message.contains("security status not satisfied")) { print("Authentication failed - check MRZ data"); } else { print("Passport error: ${e.message}"); } } on ComProviderError catch (e) { print("NFC communication error: $e"); } finally { await nfc.disconnect( iosAlertMessage: "Passport reading complete" ); } } ``` -------------------------------- ### Perform Active Authentication using Dart Source: https://context7.com/zeropass/dmrtd/llms.txt Executes an Active Authentication challenge-response sequence to verify the passport's authenticity. Generates a random 8‑byte challenge, sends it to the chip, and checks that a signature is returned. Handles errors specific to unsupported authentication and logs relevant information. ```dart import 'package:dmrtd/dmrtd.dart'; import 'dart:typed_data'; import 'dart:math'; Future performActiveAuthentication(Passport passport, NfcProvider nfc) async { try { // First read EF.DG15 to check if AA is supported nfc.setIosAlertMessage("Checking authentication support..."); final dg15 = await passport.readEfDG15(); print("Active Authentication is supported"); // Generate 8-byte random challenge final random = Random.secure(); final challenge = Uint8List(8); for (int i = 0; i < challenge.length; i++) { challenge[i] = random.nextInt(256); } print("Generated challenge: ${challenge.map((b) => b.toRadixString(16).padLeft(2, '0')).join()}"); // Send challenge to passport and get signature nfc.setIosAlertMessage("Performing authentication..."); final signature = await passport.activeAuthenticate(challenge); print("Received signature (${signature.length} bytes)"); // In production, you would verify the signature using the public key from DG // This requires additional cryptographic verification logic return signature.isNotEmpty; } on PassportError catch (e) { if (e.message.contains("not satisfied")) { print("Active Authentication not available on this passport"); } else { print("Active Authentication failed: ${e.message}"); } return false; } } ``` -------------------------------- ### Complete Passport Reading Workflow in Dart Source: https://context7.com/zeropass/dmrtd/llms.txt This Dart code demonstrates the full process of reading passport data via NFC. It handles NFC connection, authentication (BAC/PACE), reading various data groups (DG1-DG15), active authentication, and document security object. It includes error handling for communication and passport-specific errors, and provides a structured result object. ```dart import 'package:dmrtd/dmrtd.dart'; import 'dart:typed_data'; import 'dart:io'; class CompletePassportReader { Future readPassport({ required String documentNumber, required DateTime dateOfBirth, required DateTime dateOfExpiry, bool usePACE = false, }) async { final nfc = NfcProvider(); final result = PassportResult(); try { // Step 1: Check NFC availability final nfcStatus = await NfcProvider.nfcStatus; if (nfcStatus != NfcStatus.enabled) { throw Exception("NFC not enabled"); } // Step 2: Connect to passport await nfc.connect( timeout: Duration(seconds: 20), iosAlertMessage: "Hold your iPhone near Passport" ); final passport = Passport(nfc); // Step 3: Initialize session (BAC or PACE) if (usePACE) { nfc.setIosAlertMessage("Reading card capabilities..."); final cardAccess = await passport.readEfCardAccess(); final accessKey = DBAKey( documentNumber, dateOfBirth, dateOfExpiry, paceMode: true ); nfc.setIosAlertMessage("Establishing secure session..."); await passport.startSessionPACE(accessKey, cardAccess); } else { final dbaKey = DBAKey(documentNumber, dateOfBirth, dateOfExpiry); nfc.setIosAlertMessage("Establishing secure session..."); await passport.startSession(dbaKey); } // Step 4: Read EF.COM to determine available files nfc.setIosAlertMessage("Reading passport structure..."); final efCom = await passport.readEfCOM(); result.ldsVersion = efCom.version; // Step 5: Read all available data groups int progress = 20; if (efCom.dgTags.contains(EfDG1.TAG)) { nfc.setIosAlertMessage("Reading biographical data... ${progress}%"); result.dg1 = await passport.readEfDG1(); progress += 15; } if (efCom.dgTags.contains(EfDG2.TAG)) { nfc.setIosAlertMessage("Reading facial image... ${progress}%"); result.dg2 = await passport.readEfDG2(); progress += 20; } if (efCom.dgTags.contains(EfDG7.TAG)) { nfc.setIosAlertMessage("Reading signature image... ${progress}%"); result.dg7 = await passport.readEfDG7(); progress += 10; } if (efCom.dgTags.contains(EfDG11.TAG)) { nfc.setIosAlertMessage("Reading additional details... ${progress}%"); result.dg11 = await passport.readEfDG11(); progress += 10; } if (efCom.dgTags.contains(EfDG15.TAG)) { result.dg15 = await passport.readEfDG15(); // Perform Active Authentication if available nfc.setIosAlertMessage("Verifying authenticity... ${progress}%"); final challenge = Uint8List(8); result.aaSignature = await passport.activeAuthenticate(challenge); progress += 15; } // Step 6: Read EF.SOD for document verification nfc.setIosAlertMessage("Reading security object... ${progress}%"); result.sod = await passport.readEfSOD(); result.success = true; await nfc.disconnect( iosAlertMessage: "Passport read successfully!" ); } on PassportError catch (e) { result.success = false; result.error = "Passport error: ${e.message}"; await nfc.disconnect( iosErrorMessage: result.error ); } on ComProviderError catch (e) { result.success = false; result.error = "Communication error: $e"; await nfc.disconnect( iosErrorMessage: result.error ); } catch (e) { result.success = false; result.error = "Unexpected error: $e"; if (nfc.isConnected()) { await nfc.disconnect(iosErrorMessage: result.error); } } return result; } } class PassportResult { bool success = false; String? error; String? ldsVersion; EfDG1? dg1; EfDG2? dg2; EfDG7? dg7; EfDG11? dg11; EfDG15? dg15; EfSOD? sod; Uint8List? aaSignature; Map toJson() { if (!success) { return {'success': false, 'error': error}; } return { 'success': true, 'ldsVersion': ldsVersion, 'personalData': dg1 != null ? { 'firstName': dg1!.mrz.firstName, 'lastName': dg1!.mrz.lastName, 'documentNumber': dg1!.mrz.documentNumber, 'nationality': dg1!.mrz.nationality, 'dateOfBirth': dg1!.mrz.dateOfBirth.toIso8601String(), 'dateOfExpiry': dg1!.mrz.dateOfExpiry.toIso8601String(), 'gender': dg1!.mrz.gender, } : null, 'hasPhoto': dg2?.imageData != null, 'hasSignature': dg7 != null, 'hasActiveAuth': aaSignature != null, }; } } ``` -------------------------------- ### Read Passport Data Groups using Dart Source: https://context7.com/zeropass/dmrtd/llms.txt Reads and parses biographical data, facial images, and additional details from an ePassport via NFC. Utilizes the dmrtd library to access EF.COM, EF.DG1, EF.DG2, EF.DG11, EF.DG12, EF.DG15, and EF.SOD, storing results in a map. Handles missing groups and errors with appropriate messages. ```dart import 'package:dmrtd/dmrtd.dart'; import 'dart:typed_data'; Future> readPassportData(Passport passport, NfcProvider nfc) async { final passportData = {}; try { // Read EF.COM to determine available data groups nfc.setIosAlertMessage("Reading passport structure..."); final efCom = await passport.readEfCOM(); print("LDS Version: ${efCom.version}"); print("Unicode Version: ${efCom.unicodeVersion}"); print("Available Data Groups: ${efCom.dgTags}"); // Read EF.DG1 - MRZ information if (efCom.dgTags.contains(EfDG1.TAG)) { nfc.setIosAlertMessage("Reading biographical data..."); final dg1 = await passport.readEfDG1(); final mrz = dg1.mrz; passportData['firstName'] = mrz.firstName; passportData['lastName'] = mrz.lastName; Data['documentNumber'] = mrz.documentNumber; passportData['nationality'] = mrz.nationality; passportData['dateOfBirth'] = mrz.dateOfBirth; passportData['dateOfExpiry'] = mrz.dateOfExpiry; passportData['gender'] = mrz.gender; passportData['country'] = mrz.country; print("Name: ${mrz.firstName} ${mrz.lastName}"); print("Document: ${mrz.documentNumber}"); } // Read EF.DG2 - Facial image if (efCom.dgTags.contains(EfDG2.TAG)) { nfc.setIosAlertMessage("Reading facial image..."); final dg2 = await passport.readEfDG2(); if (dg2.imageData != null) { passportData['faceImage'] = dg2.imageData; passportData['imageType'] = dg2.imageType; // jpeg or jpeg2000 passportData['imageWidth'] = dg2.imageWidth; passportData['imageHeight'] = dg2.imageHeight; print("Image size: ${dg2.imageWidth}x${dg2.imageHeight}"); print("Image format: ${dg2.imageType}"); } } // Read EF.DG11 - Additional personal details if (efCom.dgTags.contains(EfDG11.TAG)) { final dg11 = await passport.readEfDG11(); passportData['additionalDetails'] = dg11; } // Read EF.DG12 - Additional document details if (efCom.dgTags.contains(EfDG12.TAG)) { final dg12 = await passport.readEfDG12(); passportData['documentDetails'] = dg; } // Read EF.DG15 - Active Authentication public key if (efCom.dgTags.contains(EfDG15.TAG)) { nfc.setIosAlertMessage("Reading security features..."); final dg15 = await passport.readEfDG15(); passportData['aaPublicKey'] = dg15; } // Read EF.SOD - Document security object nfc.setIosAlertMessage("Verifying document integrity..."); final sod = await passport.readEfSOD(); passportData['securityObject'] = sod; return passportData; } catch (e) { print("Error reading passport data: $e"); rethrow; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.