Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
NfcService.cs
1using System.Globalization;
2using System.Runtime.ExceptionServices;
3using System.Text;
4using System.Xml;
15using Waher.Events;
20using Waher.Security;
21
23{
27 [Singleton]
28 public class NfcService : INfcService
29 {
30 private readonly IAuthenticationService authenticationService = ServiceRef.Provider.GetRequiredService<IAuthenticationService>();
31
35 public NfcService()
36 : base()
37 {
38 }
39
44 public async Task TagDetected(INfcTag Tag)
45 {
46 try
47 {
48 string TagId = Hashes.BinaryToString(Tag.ID).ToUpper(CultureInfo.InvariantCulture);
49 NfcTagReference TagReference = await NfcTagReference.FindByTagId(TagId);
50
51 foreach (INfcInterface Interface in Tag.Interfaces)
52 {
53 // Some NFC devices allow all interfaces to be open, others not. So when browsing interfaces we must assure only
54 // one interface is open at a time.
55 foreach (INfcInterface Interface2 in Tag.Interfaces)
56 {
57 if (Interface2 == Interface)
58 await Interface2.OpenIfClosed();
59 else
60 Interface2.CloseIfOpen();
61 }
62
63 if (Interface is IIsoDepInterface IsoDep)
64 {
65 // ISO 14443-4
66
67 IsoDep.SetTimeout(300000); // Electronic documents may introduce latency to stall spamming. Max timeout = 5 minutes.
68
69 string Mrz = await RuntimeSettings.GetAsync("NFC.LastMrz", string.Empty);
70
71 if (!string.IsNullOrEmpty(Mrz) &&
73 {
74 StringBuilder XmlBuilder = new();
75 XmlWriter XmlOutput = XmlWriter.Create(XmlBuilder, XML.WriterSettings(false, true));
76 XmlWriterSniffer InMemoryXmlWriterSniffer = new(XmlOutput, BinaryPresentationMethod.Base64, "NFC");
77 ISniffer[] Sniffers = new ISniffer[] { InMemoryXmlWriterSniffer }.Join(
78 ServiceRef.XmppService.RemoteSniffers);
79
80 XmlOutput.WriteStartDocument();
81 XmlOutput.WriteStartElement("SnifferOutput", "http://waher.se/Schema/SnifferOutput.xsd");
82
83 InMemoryXmlWriterSniffer.Information(Mrz);
84
85 // TODO: LocalKeySeed argument must be set to the byte array of the UTF-8
86 // encodig of the PREVIEW application ID (same value that goes into the PREVIEW
87 // claim), to which the NFC.xml file will be attached, so that Neuron can
88 // cryptographically validate the readout is made just for this application,
89 // and not a replay of a previous readout.
90
91 using TravelDocumentsClient Client = new(IsoDep, DocInfo, null, Sniffers);
92
93 try
94 {
95 Client.Information("Starting readout.");
96
97 Client.StateChanged += (_, e) =>
98 {
99 // TODO: Forward state-information to UI.
100 return Task.CompletedTask;
101 };
102
103 // TODO: Seed PACE authentication with ID of PREVIEW application, so that
104 // Neuron can cryptographically validate the readout is not a replay of a
105 // previous readout.
106
107 switch (await Client.Authenticate())
108 {
109 case AuthenticateResult.Success:
110 // Authentication successful.
111 break;
112
113 case AuthenticateResult.AlreadyEncrypted:
114 // Already authenticated with the document.
115
116 case AuthenticateResult.UnableToInitializePace:
117 // Unable to initialize PACE.
118 // (Incompatibility, missing support; suggest sending log to support for troubleshooting if problem persists.)
119
120 case AuthenticateResult.UnableToAuthenticatePace:
121 // Unable to authenticate using the selected PACE protocol.
122 // (Incompatibility, missing support; suggest sending log to support for troubleshooting if problem persists.)
123
124 case AuthenticateResult.UnableToGetBacChallenge:
125 // Unable to get BAC challenge. (Probably not a valid/working travel document.)
126
127 case AuthenticateResult.BacNotImplemented:
128 // Old Travel Document requiring BAC, which is not supported.
129
130 default:
131 // TODO: Forward failure to UI.
132 return;
133 }
134
135 Client.AppInfoUpdated += (_, e) =>
136 {
137 // TODO: Forward Application-level information to UI.
138 return Task.CompletedTask;
139 };
140
141 Client.SecurityInfoUpdated += (_, e) =>
142 {
143 // TODO: Forward Security information to UI.
144 return Task.CompletedTask;
145 };
146
147 Client.MrzUpdated += (_, e) =>
148 {
149 // TODO: Forward MRZ information to UI.
150 // TODO: Compare with OCR MRZ to ensure consistency.
151 // TODO: Check ExpiryDate to ensure passport is not expired.
152 return Task.CompletedTask;
153 };
154
155 Client.BiometricEncodingFaceUpdated += (_, e) =>
156 {
157 // TODO: Remove. Now being output to get binaries for JPEG 2000 decoding.
158 if (Client.BiometricEncodingFace is not null)
159 {
160 Representation? Face = Client.BiometricEncodingFace[0].BiometricDataBlock?.Record?.Representations[0];
161
162 if (Face is not null)
163 {
164 Client.Warning("Face Image (type: " + Face.ImageDataType.ToString() + "):\r\n\r\n" +
165 Convert.ToBase64String(Face.ImageData, Base64FormattingOptions.InsertLineBreaks));
166 }
167 }
168
169 // TODO: Forward Face Biometric information to UI.
170 return Task.CompletedTask;
171 };
172
173 Client.BiometricEncodingFingersUpdated += (_, e) =>
174 {
175 // TODO: Remove. Now being output to get binaries for JPEG 2000 decoding.
176 if (Client.BiometricEncodingFingers is not null)
177 {
178 Representation? Fingers = Client.BiometricEncodingFingers[0].BiometricDataBlock?.Record?.Representations[0];
179
180 if (Fingers is not null)
181 {
182 Client.Warning("Fingers Image (type: " + Fingers.ImageDataType.ToString() + "):\r\n\r\n" +
183 Convert.ToBase64String(Fingers.ImageData, Base64FormattingOptions.InsertLineBreaks));
184 }
185 }
186
187 // TODO: Forward Fingers Biometric information to UI.
188 return Task.CompletedTask;
189 };
190
191 Client.BiometricEncodingIrisesUpdated += (_, e) =>
192 {
193 // TODO: Remove. Now being output to get binaries for JPEG 2000 decoding.
194 if (Client.BiometricEncodingIrises is not null)
195 {
196 Representation? Irises = Client.BiometricEncodingIrises[0].BiometricDataBlock?.Record?.Representations[0];
197
198 if (Irises is not null)
199 {
200 Client.Warning("Irises Image (type: " + Irises.ImageDataType.ToString() + "):\r\n\r\n" +
201 Convert.ToBase64String(Irises.ImageData, Base64FormattingOptions.InsertLineBreaks));
202 }
203 }
204
205 // TODO: Forward Irises Biometric information to UI.
206 return Task.CompletedTask;
207 };
208
209 Client.DisplayedSignaturesUpdated += (_, e) =>
210 {
211 // TODO: Remove. Now being output to get binaries for JPEG 2000 decoding.
212 if (Client.DisplayedSignatures?.Signatures is not null)
213 {
214 DisplayedSignature? Signature = Client.DisplayedSignatures?.Signatures[0];
215
216 if (Signature is not null)
217 {
218 Client.Warning("Signature Image (type: JPEG or JPEG2000):\r\n\r\n" +
219 Convert.ToBase64String(Signature.ImageData, Base64FormattingOptions.InsertLineBreaks));
220 }
221 }
222
223 // TODO: Forward Signature information to UI.
224 return Task.CompletedTask;
225 };
226
227 Client.PersonalInformationUpdated += (_, e) =>
228 {
229 // TODO: Forward Personal Information to UI.
230 return Task.CompletedTask;
231 };
232
233 switch (await Client.ReadTravelDocument(Constants.Domains.IdDomain))
234 {
235 case ReadTravelDocumentResult.Success:
236 // Readout successful.
237 break;
238
239 case ReadTravelDocumentResult.Lds1ApplicationNotFound:
240 // LDS1 eMRTD application was not found on chip. (Not an electronic passport.)
241
242 case ReadTravelDocumentResult.UnableToReadEfCom:
243 // Unable to read EF.COM. (Try again.)
244
245 case ReadTravelDocumentResult.UnableToParseEfCom:
246 // Unable to parse EF.COM. (Incompatibility, missing support; suggest sending log to support for troubleshooting if problem persists.)
247 // EF.COM used to identify services available on the chip.
248
249 case ReadTravelDocumentResult.UnableToReadEfSod:
250 // Unable to read EF.SOD. (Try again.)
251
252 case ReadTravelDocumentResult.UnableToParseEfSod:
253 // Unable to parse EF.SOD. (Incompatibility, missing support; suggest sending log to support for troubleshooting if problem persists.)
254 // EF.SOD used to identify issuers of documents.
255
256 case ReadTravelDocumentResult.UnableToReadEfDg:
257 // Unable to read EF.DGx. (Try again.)
258
259 case ReadTravelDocumentResult.UnableToParseEfDg:
260 // Unable to parse EF.DGx. (Incompatibility, missing support; suggest sending log to support for troubleshooting if problem persists.)
261 // TODO: Forward failure to UI.
262
263 case ReadTravelDocumentResult.DgHashDigestInvalid:
264 // Hash Digest as reported by EF.SOD does not match the has digest of the data group read.
265 // (Data has been corrupted, either in transit or on the passport.)
266
267 case ReadTravelDocumentResult.NoCertificates:
268 // No certificates to validate available in EF.SOD.
269 // (Not a valid Travel Document)
270
271 case ReadTravelDocumentResult.MultipleCertificates:
272 // Multiple certificates to validate available in EF.SOD were provided. Only one allowed.
273 // (Not a valid Travel Document)
274
275 case ReadTravelDocumentResult.InvalidCertificate:
276 // Certificate provided in EF.SOD is not a valid certificate.
277 // (Not a valid Travel Document)
278
279 default:
280 return;
281 }
282
283 Client.Information("Readout completed.");
284
285 await InMemoryXmlWriterSniffer.FlushAsync();
286
287 XmlOutput.WriteEndElement();
288 XmlOutput.WriteEndDocument();
289 XmlOutput.Flush();
290
291 string Xml = XmlBuilder.ToString();
292
293 // TODO: XML needs to be attached to PREVIEW ID application as an attachment
294 // named `NFC.xml` to prove that the readout was performed by this application.
295 }
296 catch (Exception ex)
297 {
298 // TODO: Forward error to UI.
299 Client.Exception(ex);
300 }
301 finally
302 {
303 IsoDep.CloseIfOpen();
304 }
305 }
306 }
307 else if (Interface is INdefInterface Ndef)
308 {
309 bool CanMakeReadOnly = await Ndef.CanMakeReadOnly();
310 bool IsWritable = await Ndef.IsWritable();
311 INdefRecord[] Records = await Ndef.GetMessage();
312
313 if (Records.Length == 0 && IsWritable)
314 {
315 await ProgramNfc(Items => Ndef.SetMessage(Items));
316 // TODO: Make read-only if able
317 }
318 else
319 {
320 foreach (INdefRecord Record in Records)
321 {
322 if (Record is INdefUriRecord UriRecord)
323 {
324 if (!string.IsNullOrEmpty(Constants.UriSchemes.GetScheme(UriRecord.Uri)))
325 {
326 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.NfcTagDetected))
327 return;
328
329 if (await App.OpenUrlAsync(UriRecord.Uri))
330 return;
331 }
332 }
333 }
334
335 // TODO: Open NFC view
336 }
337 }
338 else if (Interface is INdefFormatableInterface NdefFormatable)
339 {
340 await ProgramNfc(Items => NdefFormatable.Format(false, Items));
341 // TODO: Make read-only if able
342 }
343 else if (Interface is INfcAInterface NfcA)
344 {
345 byte[] Atqa = await NfcA.GetAtqa();
346 short Sqk = await NfcA.GetSqk();
347
348 // TODO
349 }
350 else if (Interface is INfcBInterface NfcB)
351 {
352 byte[] ApplicationData = await NfcB.GetApplicationData();
353 byte[] ProtocolInfo = await NfcB.GetProtocolInfo();
354
355 // TODO
356 }
357 else if (Interface is INfcFInterface NfcF)
358 {
359 byte[] Manufacturer = await NfcF.GetManufacturer();
360 byte[] SystemCode = await NfcF.GetSystemCode();
361
362 // TODO
363 }
364 else if (Interface is INfcVInterface NfcV)
365 {
366 sbyte DsfId = await NfcV.GetDsfId();
367 short ResponseFlags = await NfcV.GetResponseFlags();
368
369 // TODO
370 }
371 else if (Interface is INfcBarcodeInterface Barcode)
372 {
373 byte[] Data = await Barcode.ReadAllData();
374
375 // TODO
376 }
377 else if (Interface is IMifareUltralightInterface MifareUltralight)
378 {
379 byte[] Data = await MifareUltralight.ReadAllData();
380
381 // TODO
382 }
383 else if (Interface is IMifareClassicInterface MifareClassic)
384 {
385 byte[] Data = await MifareClassic.ReadAllData();
386
387 // TODO
388 }
389 }
390 }
391 catch (Exception ex)
392 {
394 }
395 }
396
397 public delegate Task<bool> WriteItems(object[] Items);
398
404 public static async Task<bool> ProgramNfc(WriteItems Callback)
405 {
406 INavigationService Nav = App.Instantiate<INavigationService>();
407 if (Nav.CurrentPage is BaseContentPage ContentPage &&
408 ContentPage.ViewModel<BaseViewModel>() is ILinkableView LinkableView &&
409 LinkableView.IsLinkable)
410 {
411 string? Link = LinkableView.Link;
412 string Title = await LinkableView.Title;
413
414 List<object> Items = [];
415
416 if (LinkableView.EncodeAppLinks)
417 Items.Add(Title);
418
419 if (!string.IsNullOrEmpty(Link))
420 Items.Add(new Uri(Link));
421
422 if (LinkableView.EncodeAppLinks)
423 {
424 Items.Add(new Uri(Constants.References.AndroidApp));
425 Items.Add(new Uri(Constants.References.IPhoneApp));
426 }
427
428 if (LinkableView.HasMedia)
429 Items.Add(new KeyValuePair<byte[], string>(LinkableView.Media!, LinkableView.MediaContentType!));
430
431 if (!await ServiceRef.Provider.GetRequiredService<IAuthenticationService>().AuthenticateUserAsync(AuthenticationPurpose.NfcTagDetected))
432 return false;
433
434 bool Ok = await Callback([.. Items]);
435
436 if (!Ok && Items[^1] is KeyValuePair<byte[], string>)
437 {
438 Items.RemoveAt(Items.Count - 1);
439 Ok = await Callback([.. Items]);
440 }
441
442 if (!Ok)
443 {
444 while (Items.Count > 2)
445 Items.RemoveAt(2);
446
447 Ok = await Callback([.. Items]);
448
449 if (!Ok)
450 {
451 Items.RemoveAt(0);
452 Ok = await Callback([.. Items]);
453 }
454 }
455
456 if (Ok)
457 {
461
462 return true;
463 }
464 else
465 {
469
470 return false;
471 }
472 }
473 else
474 return false;
475 }
476
477 }
478}
Displayed Signatures or Usual Marks (DG7). Reference: §4.7.7, EF.DG7, ICAO Doc 9303-10,...
byte[] ImageData
Raw image data of signature (May be JPEG or JPEG2000).
Contains parsed information from a machine-readable document information string.
Contains a representation of biometric data.
Contains MRZ-related Extensions for Machine-Readable Travel Documents.
Definition: MrzExtensions.cs:9
static bool ParseMrz(string MRZ, [NotNullWhen(true)] out DocumentInformation? Info)
Derives Basic Access Control Keys from the second row of the Machine-Readable string in passport (MRZ...
Represents an instance of the Neuro-Access app.
Definition: App.xaml.cs:125
const string IdDomain
Neuro-Access domain.
Definition: Constants.cs:307
References to external resources
Definition: Constants.cs:859
const string IPhoneApp
Resource where iPhone App can be downloaded.
Definition: Constants.cs:868
const string AndroidApp
Resource where Android App can be downloaded.
Definition: Constants.cs:863
static ? string GetScheme(string Url)
Gets the predefined scheme from an IoT Code
Definition: Constants.cs:200
A set of never changing property constants and helpful values.
Definition: Constants.cs:24
A strongly-typed resource class, for looking up localized strings, etc.
static string TagEngraved
Looks up a localized string similar to Tag Engraved with link to {0}..
static string TagNotEngraved
Looks up a localized string similar to Unable to engrave tag with link to {0}..
static string SuccessTitle
Looks up a localized string similar to Success.
static string ErrorTitle
Looks up a localized string similar to An error has occurred.
Near-Field Communication (NFC) Service.
Definition: NfcService.cs:29
NfcService()
Near-Field Communication (NFC) Service.
Definition: NfcService.cs:35
static async Task< bool > ProgramNfc(WriteItems Callback)
Programs an NFC tag.
Definition: NfcService.cs:404
async Task TagDetected(INfcTag Tag)
Method called when a new NFC Tag has been detected.
Definition: NfcService.cs:44
Contains information about a contact.
static Task< NfcTagReference > FindByTagId(CaseInsensitiveString TagId)
Finds information about a contact, given its Bare JID.
Base class that references services in the app.
Definition: ServiceRef.cs:43
static IServiceProvider Provider
The service provider for the app. This is set before the app is started, and will be used to resolve ...
Definition: ServiceRef.cs:48
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:130
static IReportingStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:370
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:190
A base class for all pages, intended for custom navigation with explicit life-cycle events.
A base class for all view models, inheriting from the BindableObject. NOTE: using this class requir...
Helps with common XML-related tasks.
Definition: XML.cs:21
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Definition: XML.cs:1351
async Task FlushAsync()
Waits until pending sniffer events have been processed.
Definition: SnifferBase.cs:425
void Information(string Comment)
Called to inform the viewer of something.
Definition: SnifferBase.cs:305
Outputs sniffed data to an XML writer.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Definition: Hashes.cs:63
ISO DEP interface, for communication with an NFC Tag.
Mifare Classic interface, for communication with an NFC Tag.
Mifare Ultralight interface, for communication with an NFC Tag.
NDEF Formatable interface, for communication with an NFC Tag.
NDEF interface, for communication with an NFC Tag.
NFC A interface, for communication with an NFC Tag.
NFC B interface, for communication with an NFC Tag.
NFC Barcode interface, for communication with an NFC Tag.
NFC F interface, for communication with an NFC Tag.
Specific Interface (technology) for communication with an NFC Tag.
void CloseIfOpen()
Closes the interface, if connected.
Task OpenIfClosed()
Connects the interface, if not connected.
Interface for an NFC Tag.
Definition: INfcTag.cs:9
byte[] ID
NFC Tag ID
Definition: INfcTag.cs:14
INfcInterface[] Interfaces
Communication interfaces available on the NFC Tag.
Definition: INfcTag.cs:22
NFC V interface, for communication with an NFC Tag.
Interface for NDEF records
Definition: INdefRecord.cs:7
Interface for NDEF URI records
Interface for the Near-Field Communication (NFC) Service.
Definition: INfcService.cs:11
Service for navigating between pages using route-based navigation.
BaseContentPage? CurrentPage
Gets the current visible view.
Task DisplayException(Exception Exception, string? Title=null)
Displays an alert/message box to the user.
Task< bool > DisplayAlert(string Title, string Message, string? Accept=null, string? Cancel=null)
Displays an alert/message box to the user.
Interface for linkable views.
Definition: ILinkableView.cs:7
bool IsLinkable
If the current view is linkable.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:10
Definition: ImplTypes.g.cs:58
ReadTravelDocumentResult
Enumerations of possible results when reading a travel document.
AuthenticateResult
Enumerations of possible results when authenticating the app with the travel document.
AuthenticationPurpose
Purpose for requesting the user to authenticate itself.
BinaryPresentationMethod
How binary data is to be presented.