flutter_cccd_vn 2.0.0
flutter_cccd_vn: ^2.0.0 copied to clipboard
ICAO Doc 9303 MRTD (biometric passport) Dart library with a Vietnamese CCCD (căn cước công dân) NFC reading adapter — BAC/PACE, OCR-driven credentials, and a typed CCCD data/verification API.
Dart library for ICAO Machine Readable Travel Documents standard - Biometric Passport #
DMRTD is dart implementation of ICAO 9303 standard. Library provide APIs to send commands to and read data from MRTD.
Key features #
- PACE & BAC session key establishment protocol
- Reading all elementary files from MRTD, e.g.: EF.DG1, EF.DG2, EF.DG11, EF.DG12, EF.DG15 ...
Note: most of files are not fully parsed yet. - Executing
Active Authenticationon MRTD (i.e.: sign arbitrary data with passport) - Basic implementation of ICC ISO7816-4 smart card standard
- Implementation of ISO 9797 Algorithm 3 MAC and padding scheme
Library structure #
flutter_cccd_vn.dart (lib/dmrtd.dart) - public passport API
extensions.dart - exposes library's dart extensions
internal.dart - exposes internal components of the library such as MrtdApi, ICC and crypto
Usage #
- Include
flutter_cccd_vnlibrary in your project'spubspec.yamlfile:
dependencies:
flutter_cccd_vn:
path: '<path_to_flutter_cccd_vn_folder>'
- Run
flutter pub get
Example:
Note: See also example app
import 'package:flutter_cccd_vn/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));
}
}
Other documentation #
- ICAO 9303 Specifications Common to all MRTDs
- ICAO 9303 Specifications for Machine Readable Passports (MRPs) and other TD3 Size MRTDs
- ICAO 9303 eMRTD logical data structure
- ICAO 9303 Security mechanisms for MRTDs
License #
This project is licensed under the terms of the GNU Lesser General Public License (LGPL) for open-source use and a Commercial License for proprietary use. See the LICENSE.LGPL and LICENSE.COMMERCIAL files for details.
CCCD Việt Nam API #
The package includes an ICAO 9303 adapter for the Vietnamese citizen ID flow described by the card profile:
import 'package:flutter_cccd_vn/cccd_vietnam.dart';
final nfc = NfcProvider();
final reader = CccdReader(
transport: nfc,
protocol: IcaoCccdProtocol(),
operationTimeout: const Duration(seconds: 30),
maxRetries: 1,
);
await reader.connect();
final data = await reader.read(CccdCan('123456'));
await reader.disconnect();
CccdCan uses EF.CardAccess + PACE. CccdMrz uses BAC. The adapter reads DG1 and optionally DG2/DG13 according to EF.COM. EAC/DG3/DG4 and real-card compatibility still require physical-card validation and terminal credentials. Do not log CAN values, MRZ values or card payloads.
For a faster identity-only read, skip optional files:
protocol: IcaoCccdProtocol(
includePortrait: false,
includeDg13: false,
),
The reader classifies NFC disabled, timeout, tag lost, cancellation, connection, authentication, parse, integrity and busy errors. Retries apply only to completed transient connection failures; timeout cannot be safely retried because Dart cannot cancel an in-flight NFC Future. After a timeout, the reader is poisoned: call disconnect() for cleanup and create a new CccdReader before retrying. Tag-lost and user-cancelled operations require a new user interaction.
NFC platform setup #
Android example already declares NFC permission and hardware support in example/android/app/src/main/AndroidManifest.xml.
For iOS host apps, enable the Near Field Communication Tag Reader Session Formats capability, add NFCReaderUsageDescription to Info.plist, and add the ISO7816 reader entitlement required by the app's provisioning profile. The repository does not contain an example/ios target.
Local CCCD capture, OCR and face verification #
The example app uses camera, ML Kit text recognition, ML Kit face detection and exact face_verification 0.3.8 to keep capture, OCR and FaceNet comparison on-device. The root package only exports the pure Dart contracts: CccdOcrParser, CccdFaceMatchResult and CccdVerificationResult.
The local face score is a similarity signal, not liveness detection or legal identity proof. CccdVerificationResult reports consistency, not identity verification. Calibrate the threshold with real devices and lighting before production. The app deletes temporary images and the temporary face template after each attempt and does not log OCR, portrait, selfie or embeddings. face_verification may emit its own internal debug score/ID logs; production deployments requiring strict biometric log policy should use a fork/configuration with those logs disabled.
The current example verification flow is Android-only because this repository has no example/ios host target. An iOS consuming app must add NSCameraUsageDescription, NFC ISO7816 entitlements and use iOS 15.5+ for the selected face stack. DG2 JPEG2000 is reported as invalidPortrait; only JPEG is passed to the matcher. The selected face package is pinned to 0.3.8, OCR to ML Kit 0.16.0, face detection to 0.14.0, and the root archive dependency is 4.0.9. See face_verification, camera, ML Kit text recognition and ML Kit face detection.
On a real Android device: run cd example && flutter pub get && flutter run, open Xác minh CCCD local, capture both card sides, enter the six-digit CAN, tap Đọc NFC DG1 + DG2, then capture one selfie. The result is matched, rejected or needsReview; it is not liveness or legal identity verification.