gascom/lib/screens/nfc_screen.dart
Abdullah Salah 216efb8a83 first commit
2024-12-25 11:09:55 +03:00

586 lines
20 KiB
Dart

import 'dart:typed_data';
import 'package:dmrtd/dmrtd.dart';
import 'package:dmrtd/src/proto/can_key.dart';
import 'package:flutter/material.dart';
import 'package:gascom/widgets/custom_app_bar.dart';
class NfcScreen extends StatefulWidget {
const NfcScreen({super.key});
@override
State<NfcScreen> createState() => _NfcScreenState();
}
class _NfcScreenState extends State<NfcScreen> {
final NfcProvider _nfc = NfcProvider();
MrtdData? _mrtdData = MrtdData();
String _alertMessage = "";
bool _isReading = false;
String id = ''; // should change based on user id card (it's the string below the image)
DateTime dateOfBirth = DateTime(2000, 1, 1); // should change based on user id card
DateTime dateOfExpiry = DateTime(2029, 1, 1); // should change based on user id card
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
try {
_buttonPressed();
} catch (e) {
print(e);
}
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: CustomAppBar(),
body: SafeArea(
child: Center(
child: Text("NFC Screen, Nfc has ti be turned on."),
),
),
);
}
String formatProgressMsg(String message, int percentProgress) {
final p = (percentProgress / 20).round();
final full = "🟢 " * p;
final empty = "⚪️ " * (5 - p);
return "$message\n\n$full$empty";
}
void _readMRTD({required AccessKey accessKey, bool isPace = false}) async {
try {
setState(() {
_mrtdData = null;
_alertMessage = "Waiting for Passport tag ...";
_isReading = true;
});
try {
bool demo = false;
print("-------------- FIRST -------------");
if (!demo)
await _nfc.connect(
iosAlertMessage: "Hold your phone near Biometric Passport");
print("-------------- SECOND -------------");
final passport = Passport(_nfc);
print("-------------- THIRD -------------");
setState(() {
_alertMessage = "Reading Passport ...";
});
print("-------------- 4 -------------");
_nfc.setIosAlertMessage("Trying to read EF.CardAccess ...");
final mrtdData = MrtdData();
print("-------------- 5 -------------");
try {
mrtdData.cardAccess = await passport.readEfCardAccess();
} on PassportError {
print("-------------- 6 -------------");
//if (e.code != StatusWord.fileNotFound) rethrow;
}
_nfc.setIosAlertMessage("Trying to read EF.CardSecurity ...");
try {
//mrtdData.cardSecurity = await passport.readEfCardSecurity();
} on PassportError {
//if (e.code != StatusWord.fileNotFound) rethrow;
}
_nfc.setIosAlertMessage("Initiating session with PACE...");
//set MrtdData
mrtdData.isPACE = isPace;
mrtdData.isDBA = accessKey.PACE_REF_KEY_TAG == 0x01 ? true : false;
if (isPace) {
//PACE session
print("-------------- 7 -------------");
await passport.startSessionPACE(accessKey, mrtdData.cardAccess!);
} else {
print("-------------- 8 -------------");
//BAC session
await passport.startSession(accessKey as DBAKey);
}
print("-------------- 9 -------------");
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.COM ...", 0));
mrtdData.com = await passport.readEfCOM();
_nfc.setIosAlertMessage(
formatProgressMsg("Reading Data Groups ...", 20));
print("-------------- 10 -------------");
if (mrtdData.com!.dgTags.contains(EfDG1.TAG)) {
mrtdData.dg1 = await passport.readEfDG1();
print("DG1 mrz country: ${mrtdData.dg1?.mrz.country}");
print("DG1 mrz dateOfBirth: ${mrtdData.dg1?.mrz.dateOfBirth}");
print("DG1 mrz dateOfExpiry: ${mrtdData.dg1?.mrz.dateOfExpiry}");
print("DG1 mrz documentCode: ${mrtdData.dg1?.mrz.documentCode}");
print("DG1 mrz documentNumber: ${mrtdData.dg1?.mrz.documentNumber}");
print("DG1 mrz firstName: ${mrtdData.dg1?.mrz.firstName}");
print("DG1 mrz gender: ${mrtdData.dg1?.mrz.gender}");
print("DG1 mrz lastName: ${mrtdData.dg1?.mrz.lastName}");
print("DG1 mrz nationality: ${mrtdData.dg1?.mrz.nationality}");
print("DG1 mrz optionalData: ${mrtdData.dg1?.mrz.optionalData}");
print("DG1 mrz optionalData2: ${mrtdData.dg1?.mrz.optionalData2}");
print("DG1 mrz version: ${mrtdData.dg1?.mrz.version}");
}
print("-------------- 11 -------------");
if (mrtdData.com!.dgTags.contains(EfDG2.TAG)) {
mrtdData.dg2 = await passport.readEfDG2();
print("DG2 deviceType: ${mrtdData.dg2?.deviceType}");
print("DG2 expression: ${mrtdData.dg2?.expression}");
print("DG2 eyeColor: ${mrtdData.dg2?.eyeColor}");
print("DG2 faceImageType: ${mrtdData.dg2?.faceImageType}");
print("DG2 facialRecordDataLength: ${mrtdData.dg2?.facialRecordDataLength}");
print("DG2 featureMask: ${mrtdData.dg2?.featureMask}");
print("DG2 gender: ${mrtdData.dg2?.gender}");
print("DG2 hairColor: ${mrtdData.dg2?.hairColor}");
print("DG2 imageColorSpace: ${mrtdData.dg2?.imageColorSpace}");
print("DG2 imageData: ${mrtdData.dg2?.imageData}");
print("DG2 imageHeight: ${mrtdData.dg2?.imageHeight}");
print("DG2 imageType: ${mrtdData.dg2?.imageType}");
print("DG2 imageWidth: ${mrtdData.dg2?.imageWidth}");
print("DG2 lengthOfRecord: ${mrtdData.dg2?.lengthOfRecord}");
print("DG2 nrFeaturePoints: ${mrtdData.dg2?.nrFeaturePoints}");
print("DG2 numberOfFacialImages: ${mrtdData.dg2?.numberOfFacialImages}");
print("DG2 poseAngle: ${mrtdData.dg2?.poseAngle}");
print("DG2 poseAngleUncertainty: ${mrtdData.dg2?.poseAngleUncertainty}");
print("DG2 quality: ${mrtdData.dg2?.quality}");
print("DG2 sourceType: ${mrtdData.dg2?.sourceType}");
print("DG2 versionNumber: ${mrtdData.dg2?.versionNumber}");
}
// To read DG3 and DG4 session has to be established with CVCA certificate (not supported).
// if(mrtdData.com!.dgTags.contains(EfDG3.TAG)) {
// mrtdData.dg3 = await passport.readEfDG3();
// }
// if(mrtdData.com!.dgTags.contains(EfDG4.TAG)) {
// mrtdData.dg4 = await passport.readEfDG4();
// }
if (mrtdData.com!.dgTags.contains(EfDG5.TAG)) {
mrtdData.dg5 = await passport.readEfDG5();
}
if (mrtdData.com!.dgTags.contains(EfDG6.TAG)) {
mrtdData.dg6 = await passport.readEfDG6();
}
if (mrtdData.com!.dgTags.contains(EfDG7.TAG)) {
mrtdData.dg7 = await passport.readEfDG7();
}
if (mrtdData.com!.dgTags.contains(EfDG8.TAG)) {
mrtdData.dg8 = await passport.readEfDG8();
}
if (mrtdData.com!.dgTags.contains(EfDG9.TAG)) {
mrtdData.dg9 = await passport.readEfDG9();
}
if (mrtdData.com!.dgTags.contains(EfDG10.TAG)) {
mrtdData.dg10 = await passport.readEfDG10();
}
if (mrtdData.com!.dgTags.contains(EfDG11.TAG)) {
mrtdData.dg11 = await passport.readEfDG11();
print("DG11 custodyInformation: ${mrtdData.dg11?.custodyInformation}");
print("DG11 ersonalSummary: ${mrtdData.dg11?.ersonalSummary}");
print("DG11 fullDateOfBirth: ${mrtdData.dg11?.fullDateOfBirth}");
print("DG11 nameOfHolder: ${mrtdData.dg11?.nameOfHolder}");
print("DG11 otherNames: ${mrtdData.dg11?.otherNames}");
print("DG11 otherValidTDNumbers: ${mrtdData.dg11?.otherValidTDNumbers}");
print("DG11 permanentAddress: ${mrtdData.dg11?.permanentAddress}");
print("DG11 personalNumber: ${mrtdData.dg11?.personalNumber}");
print("DG11 placeOfBirth: ${mrtdData.dg11?.placeOfBirth}");
print("DG11 profession: ${mrtdData.dg11?.profession}");
print("DG11 proofOfCitizenship: ${mrtdData.dg11?.proofOfCitizenship}");
print("DG11 telephone: ${mrtdData.dg11?.telephone}");
print("DG11 title: ${mrtdData.dg11?.title}");
}
if (mrtdData.com!.dgTags.contains(EfDG12.TAG)) {
mrtdData.dg12 = await passport.readEfDG12();
print("DG12 dateOfIssue: ${mrtdData.dg12?.dateOfIssue}");
print("DG12 dateOfIssue: ${mrtdData.dg12?.issuingAuthority}");
}
if (mrtdData.com!.dgTags.contains(EfDG13.TAG)) {
mrtdData.dg13 = await passport.readEfDG13();
}
if (mrtdData.com!.dgTags.contains(EfDG14.TAG)) {
mrtdData.dg14 = await passport.readEfDG14();
}
if (mrtdData.com!.dgTags.contains(EfDG15.TAG)) {
mrtdData.dg15 = await passport.readEfDG15();
_nfc.setIosAlertMessage(formatProgressMsg("Doing AA ...", 60));
print("DG15 aaPublicKey: ${mrtdData.dg15?.aaPublicKey}");
mrtdData.aaSig = await passport.activeAuthenticate(Uint8List(8));
}
if (mrtdData.com!.dgTags.contains(EfDG16.TAG)) {
mrtdData.dg16 = await passport.readEfDG16();
}
print("-------------- 12 -------------");
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.SOD ...", 80));
mrtdData.sod = await passport.readEfSOD();
setState(() {
_mrtdData = mrtdData;
});
setState(() {
_alertMessage = "";
});
} 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!";
}
print("PassportError: ${e.message}");
} else {
print(
"An exception was encountered while trying to read Passport: $e");
}
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 = "";
}
setState(() {
_alertMessage = alertMsg;
});
} finally {
if (_alertMessage.isNotEmpty) {
await _nfc.disconnect(iosErrorMessage: _alertMessage);
} else {
await _nfc.disconnect(
iosAlertMessage: formatProgressMsg("Finished", 100));
}
setState(() {
_isReading = false;
});
}
} on Exception catch (e) {
print("Read MRTD error: $e");
}
}
void _readMRTDOld() async {
try {
setState(() {
_mrtdData = null;
_alertMessage = "Waiting for Passport tag ...";
_isReading = true;
});
await _nfc.connect(
iosAlertMessage: "Hold your phone near Biometric Passport");
final passport = Passport(_nfc);
setState(() {
_alertMessage = "Reading Passport ...";
});
_nfc.setIosAlertMessage("Trying to read EF.CardAccess ...");
final mrtdData = MrtdData();
try {
mrtdData.cardAccess = await passport.readEfCardAccess();
} on PassportError {
//if (e.code != StatusWord.fileNotFound) rethrow;
}
_nfc.setIosAlertMessage("Trying to read EF.CardSecurity ...");
try {
mrtdData.cardSecurity = await passport.readEfCardSecurity();
} on PassportError {
//if (e.code != StatusWord.fileNotFound) rethrow;
}
_nfc.setIosAlertMessage("Initiating session ...");
final bacKeySeed = DBAKey(id, dateOfBirth, dateOfExpiry);
await passport.startSession(bacKeySeed);
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.COM ...", 0));
mrtdData.com = await passport.readEfCOM();
_nfc.setIosAlertMessage(formatProgressMsg("Reading Data Groups ...", 20));
if (mrtdData.com!.dgTags.contains(EfDG1.TAG)) {
mrtdData.dg1 = await passport.readEfDG1();
print(mrtdData.dg1?.mrz);
}
if (mrtdData.com!.dgTags.contains(EfDG2.TAG)) {
mrtdData.dg2 = await passport.readEfDG2();
print("DG2 deviceType: ${mrtdData.dg2?.deviceType}");
print("DG2 expression: ${mrtdData.dg2?.expression}");
print("DG2 eyeColor: ${mrtdData.dg2?.eyeColor}");
print("DG2 faceImageType: ${mrtdData.dg2?.faceImageType}");
print("DG2 facialRecordDataLength: ${mrtdData.dg2?.facialRecordDataLength}");
print("DG2 featureMask: ${mrtdData.dg2?.featureMask}");
print("DG2 fid: ${mrtdData.dg2?.fid}");
print("DG2 gender: ${mrtdData.dg2?.gender}");
print("DG2 hairColor: ${mrtdData.dg2?.hairColor}");
print("DG2 imageColorSpace: ${mrtdData.dg2?.imageColorSpace}");
print("DG2 imageData: ${mrtdData.dg2?.imageData}");
print("DG2 imageHeight: ${mrtdData.dg2?.imageHeight}");
print("DG2 imageType: ${mrtdData.dg2?.imageType}");
print("DG2 imageWidth: ${mrtdData.dg2?.imageWidth}");
print("DG2 nrFeaturePoints: ${mrtdData.dg2?.nrFeaturePoints}");
print("DG2 numberOfFacialImages: ${mrtdData.dg2?.numberOfFacialImages}");
print("DG2 lengthOfRecord: ${mrtdData.dg2?.lengthOfRecord}");
print("DG2 poseAngle: ${mrtdData.dg2?.poseAngle}");
print("DG2 poseAngleUncertainty: ${mrtdData.dg2?.poseAngleUncertainty}");
print("DG2 quality: ${mrtdData.dg2?.quality}");
print("DG2 sourceType: ${mrtdData.dg2?.sourceType}");
print("DG2 versionNumber: ${mrtdData.dg2?.versionNumber}");
print("DG2 tag: ${mrtdData.dg2?.tag}");
print("DG2 sfi: ${mrtdData.dg2?.sfi}");
}
// To read DG3 and DG4 session has to be established with CVCA certificate (not supported).
// if(mrtdData.com!.dgTags.contains(EfDG3.TAG)) {
// mrtdData.dg3 = await passport.readEfDG3();
// }
// if(mrtdData.com!.dgTags.contains(EfDG4.TAG)) {
// mrtdData.dg4 = await passport.readEfDG4();
// }
if (mrtdData.com!.dgTags.contains(EfDG5.TAG)) {
mrtdData.dg5 = await passport.readEfDG5();
}
if (mrtdData.com!.dgTags.contains(EfDG6.TAG)) {
mrtdData.dg6 = await passport.readEfDG6();
}
if (mrtdData.com!.dgTags.contains(EfDG7.TAG)) {
mrtdData.dg7 = await passport.readEfDG7();
}
if (mrtdData.com!.dgTags.contains(EfDG8.TAG)) {
mrtdData.dg8 = await passport.readEfDG8();
}
if (mrtdData.com!.dgTags.contains(EfDG9.TAG)) {
mrtdData.dg9 = await passport.readEfDG9();
}
if (mrtdData.com!.dgTags.contains(EfDG10.TAG)) {
mrtdData.dg10 = await passport.readEfDG10();
}
if (mrtdData.com!.dgTags.contains(EfDG11.TAG)) {
mrtdData.dg11 = await passport.readEfDG11();
print("DG11 custodyInformation: ${mrtdData.dg11?.custodyInformation}");
print("DG11 ersonalSummary: ${mrtdData.dg11?.ersonalSummary}");
print("DG11 fullDateOfBirth: ${mrtdData.dg11?.fullDateOfBirth}");
print("DG11 nameOfHolder: ${mrtdData.dg11?.nameOfHolder}");
print("DG11 otherNames: ${mrtdData.dg11?.otherNames}");
print("DG11 otherValidTDNumbers: ${mrtdData.dg11?.otherValidTDNumbers}");
print("DG11 permanentAddress: ${mrtdData.dg11?.permanentAddress}");
print("DG11 personalNumber: ${mrtdData.dg11?.personalNumber}");
print("DG11 placeOfBirth: ${mrtdData.dg11?.placeOfBirth}");
print("DG11 profession: ${mrtdData.dg11?.profession}");
print("DG11 proofOfCitizenship: ${mrtdData.dg11?.proofOfCitizenship}");
print("DG11 telephone: ${mrtdData.dg11?.telephone}");
print("DG11 title: ${mrtdData.dg11?.title}");
}
if (mrtdData.com!.dgTags.contains(EfDG12.TAG)) {
mrtdData.dg12 = await passport.readEfDG12();
print("DG12 dateOfIssue: ${mrtdData.dg12?.dateOfIssue}");
print("DG12 issuingAuthority: ${mrtdData.dg12?.issuingAuthority}");
}
if (mrtdData.com!.dgTags.contains(EfDG13.TAG)) {
mrtdData.dg13 = await passport.readEfDG13();
}
if (mrtdData.com!.dgTags.contains(EfDG14.TAG)) {
mrtdData.dg14 = await passport.readEfDG14();
}
if (mrtdData.com!.dgTags.contains(EfDG15.TAG)) {
mrtdData.dg15 = await passport.readEfDG15();
print("DG12 issuingAuthority: ${mrtdData.dg15?.aaPublicKey}");
_nfc.setIosAlertMessage(formatProgressMsg("Doing AA ...", 60));
mrtdData.aaSig = await passport.activeAuthenticate(Uint8List(8));
}
if (mrtdData.com!.dgTags.contains(EfDG16.TAG)) {
mrtdData.dg16 = await passport.readEfDG16();
}
_nfc.setIosAlertMessage(formatProgressMsg("Reading EF.SOD ...", 80));
mrtdData.sod = await passport.readEfSOD();
setState(() {
_mrtdData = mrtdData;
});
setState(() {
_alertMessage = "";
});
} 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!";
}
print("PassportError: ${e.message}");
} else {
print("An exception was encountered while trying to read Passport: $e");
}
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 = "";
}
setState(() {
_alertMessage = alertMsg;
});
} finally {
if (_alertMessage.isNotEmpty) {
await _nfc.disconnect(iosErrorMessage: _alertMessage);
} else {
await _nfc.disconnect(
iosAlertMessage: formatProgressMsg("Finished", 100));
}
setState(() {
_isReading = false;
});
}
}
void _buttonPressed() async {
String _can = "";
print("Button pressed");
//Check on what tab we are
if (true) {
//DBA tab
// String errorText = "";
// if (_doe.text.isEmpty) {
// errorText += "Please enter date of expiry!\n";
// }
// if (_dob.text.isEmpty) {
// errorText += "Please enter date of birth!\n";
// }
// if (_docNumber.text.isEmpty) {
// errorText += "Please enter passport number!";
// }
// setState(() {
// _alertMessage = errorText;
// });
// //If there is an error, just jump out of the function
// if (errorText.isNotEmpty) return;
final bacKeySeed = DBAKey(id, dateOfBirth, dateOfExpiry, paceMode: true);
_readMRTD(accessKey: bacKeySeed, isPace: true);
} else {
// PACE tab
// String errorText = "";
// if (_can.isEmpty) {
// errorText = "Please enter CAN number!";
// } else if (_can.text.length != 6) {
// errorText = "CAN number must be exactly 6 digits long!";
// }
// setState(() {
// _alertMessage = errorText;
// });
// //If there is an error, just jump out of the function
// if (errorText.isNotEmpty) return;
// final canKeySeed = CanKey(_can.text);
final canKeySeed = CanKey(id);
_readMRTD(accessKey: canKeySeed, isPace: true);
}
}
}
class MrtdData {
EfCardAccess? cardAccess;
EfCardSecurity? cardSecurity;
EfCOM? com;
EfSOD? sod;
EfDG1? dg1;
EfDG2? dg2;
EfDG3? dg3;
EfDG4? dg4;
EfDG5? dg5;
EfDG6? dg6;
EfDG7? dg7;
EfDG8? dg8;
EfDG9? dg9;
EfDG10? dg10;
EfDG11? dg11;
EfDG12? dg12;
EfDG13? dg13;
EfDG14? dg14;
EfDG15? dg15;
EfDG16? dg16;
Uint8List? aaSig;
bool? isPACE;
bool? isDBA;
}
final Map<DgTag, String> dgTagToString = {
EfDG1.TAG: 'EF.DG1',
EfDG2.TAG: 'EF.DG2',
EfDG3.TAG: 'EF.DG3',
EfDG4.TAG: 'EF.DG4',
EfDG5.TAG: 'EF.DG5',
EfDG6.TAG: 'EF.DG6',
EfDG7.TAG: 'EF.DG7',
EfDG8.TAG: 'EF.DG8',
EfDG9.TAG: 'EF.DG9',
EfDG10.TAG: 'EF.DG10',
EfDG11.TAG: 'EF.DG11',
EfDG12.TAG: 'EF.DG12',
EfDG13.TAG: 'EF.DG13',
EfDG14.TAG: 'EF.DG14',
EfDG15.TAG: 'EF.DG15',
EfDG16.TAG: 'EF.DG16'
};