3using System.Diagnostics.CodeAnalysis;
4using System.Globalization;
6using System.Reflection;
8using System.Threading.Tasks;
42 private static readonly Dictionary<ushort, IDataObject> dataObjects = GetDataObjects();
53 private readonly
byte[]? localKeySeed = LocalKeySeed;
56 private CMac? cMac =
null;
57 private byte[]? ks_Enc =
null;
58 private byte[]? ks_Mac =
null;
59 private byte[]? sendSequenceCounter =
null;
60 private byte[]? zeroIv =
null;
61 private bool encrypted =
false;
62 private bool enhancedSecurity =
false;
63 private bool permitPlatformDependentValidation =
true;
64 private bool disposed =
false;
75 if (this.ks_Enc is not
null)
77 Array.Clear(this.ks_Enc, 0, this.ks_Enc.Length);
81 if (this.ks_Mac is not
null)
83 Array.Clear(this.ks_Mac, 0, this.ks_Mac.Length);
88 this.encrypted =
false;
90 this.tagInterface.CloseIfOpen();
100 set => this.appInfo = value;
106 public bool PermitPlatformDependentValidation
108 get => this.permitPlatformDependentValidation;
109 set => this.permitPlatformDependentValidation = value;
194 return this.SetState(NewState,
null);
199 this.state = NewState;
206 public event EventHandlerAsync<TravelDocumentsStateEventArgs>? StateChanged;
212 public async Task<bool> SelectMaster()
218 if (this.HasSniffers)
219 this.Information(
"SelectMaster()");
232 byte[] Response = await this.ExecuteCommand(Command);
234 return this.CheckResponse(Response);
242 public async Task<bool> SelectApplication(
byte[] ApplicationId)
248 if (this.HasSniffers)
258 (
byte)ApplicationId.Length
262 byte[] Response = await this.ExecuteCommand(Command);
264 return this.CheckResponse(Response);
272 public async Task<bool> SelectFile(ushort FileId)
278 if (this.HasSniffers)
279 this.Information(
"SelectFile(" + FileId.ToString(
"X4", CultureInfo.InvariantCulture) +
")");
292 byte[] Response = await this.ExecuteCommand(Command);
294 return this.CheckResponse(Response);
297 private async Task<byte[]> ExecuteCommand(
byte[] Command)
299 byte[] Response = await this.ExecuteCommandSingle(Command);
300 return await this.GetRemainingResponseData(Response);
303 private async Task<byte[]> ExecuteCommandSingle(
byte[] Command)
306 return await this.tagInterface.ExecuteCommand(Command,
this);
310 if (this.HasSniffers)
314 throw new ArgumentException(
"Command too short.", nameof(Command));
332 throw new ArgumentException(
"Command data length exceeds command length.", nameof(Command));
334 Le = Lc + 5 < Command.Length ?
Command[Lc + 5] : (byte)0;
344 byte[] HeaderPadding =
new byte[BlockSize - 4];
345 HeaderPadding[0] = 0x80;
347 int PaddedDataLen = (Lc + BlockSize - 1) & ~(BlockSize - 1);
349 if (PaddedDataLen + 17 >
byte.MaxValue)
350 throw new ArgumentException(
"Command data too long.", nameof(Command));
352 byte[] PaddedData =
new byte[PaddedDataLen];
355 Buffer.BlockCopy(Command, 5, PaddedData, 0, Lc);
357 if (Lc < PaddedDataLen)
358 PaddedData[Lc] = 0x80;
360 this.IncrementCounter();
362 if (this.HasSniffers)
365 byte[] IV = this.protocol.
Encrypt(this.ks_Enc!, this.zeroIv!, this.sendSequenceCounter!);
367 if (this.HasSniffers)
373 byte[] EncryptedData = this.protocol.
Encrypt(this.ks_Enc!, IV, PaddedData);
375 if (this.HasSniffers)
385 byte[] FooterPadding =
new byte[BlockSize - 3];
386 FooterPadding[0] = 0x80;
388 byte[] EncryptedDataHeader = PaddedDataLen == 0 ? [] :
390 (INS & 1) == 0 ? (
byte)0x87 : (byte)0x85,
391 (
byte)(PaddedDataLen + 1),
395 int AssociatedDataPadLen = (EncryptedDataHeader.Length + Footer.Length) % BlockSize;
396 byte[] AssociatedDataPadding;
398 if (AssociatedDataPadLen == 0)
399 AssociatedDataPadding = [];
402 AssociatedDataPadding =
new byte[BlockSize - AssociatedDataPadLen];
403 AssociatedDataPadding[0] = 0x80;
406 byte[] AssociatedData = CONCAT(
407 this.sendSequenceCounter!,
413 AssociatedDataPadding);
415 if (this.HasSniffers)
418 byte[] Signature = this.cMac!.
Sign(AssociatedData, 8);
420 byte[] EncryptedCommand = CONCAT(
422 [(
byte)(EncryptedDataHeader.Length + EncryptedData.Length + Footer.Length + 10)],
435 byte[] Response = await this.tagInterface.ExecuteCommand(EncryptedCommand,
this);
436 int c = Response.Length - 2;
439 return Response ?? [];
441 byte[]? EncryptedResponseData =
null;
442 byte[]? ResponseSignature =
null;
444 byte SW1 = Response[c];
445 byte SW2 = Response[c + 1];
446 int StartOfSignature = 0;
450 switch (Response[i++])
455 this.UnexpectedEndOfResponse();
459 int L = Response[i++];
466 this.UnexpectedEndOfResponse();
476 this.UnexpectedEndOfResponse();
492 this.UnexpectedEndOfResponse();
498 this.Error(
"Expected length of DO'87' block.");
502 byte PaddingByte = Response[i++];
504 if (PaddingByte != 1 && PaddingByte != 2)
506 this.Error(
"Expected 01 or 02 as padding byte in DO'87' block.");
513 this.UnexpectedEndOfResponse();
517 EncryptedResponseData =
new byte[L];
518 Buffer.BlockCopy(Response, i, EncryptedResponseData, 0, L);
521 if (PaddingByte == 2)
523 if (i < c && Response[i] == 0x80)
527 while (i < c && Response[i] == 0x00)
536 this.UnexpectedEndOfResponse();
544 this.Error(
"Expected DO'99' block to have a length of 02.");
550 this.UnexpectedEndOfResponse();
559 StartOfSignature = i - 1;
563 this.UnexpectedEndOfResponse();
571 this.Error(
"Expected DO'8E' block to have a length of 08.");
577 this.UnexpectedEndOfResponse();
581 ResponseSignature =
new byte[L];
582 Buffer.BlockCopy(Response, i, ResponseSignature, 0, L);
587 this.Error(
"Unexpected DO block: " + Response[i - 1].
ToString(
"X2", CultureInfo.InvariantCulture));
592 if (ResponseSignature is
null)
594 this.Error(
"Missing DO'8E' block with response signature.");
598 int ResponsePadLength = StartOfSignature % BlockSize;
599 byte[] ResponsePadding;
601 if (ResponsePadLength == 0)
602 ResponsePadding = [];
605 ResponsePadding =
new byte[BlockSize - ResponsePadLength];
606 ResponsePadding[0] = 0x80;
609 this.IncrementCounter();
611 AssociatedData =
new byte[StartOfSignature];
612 Buffer.BlockCopy(Response, 0, AssociatedData, 0, StartOfSignature);
614 AssociatedData = CONCAT(
615 this.sendSequenceCounter!,
619 if (this.HasSniffers)
622 if (!this.cMac.
Verify(AssociatedData, ResponseSignature))
624 this.Error(
"Invalid response signature.");
628 if (EncryptedResponseData is
null)
629 Response = [SW1, SW2];
632 IV = this.protocol.
Encrypt(this.ks_Enc!, this.zeroIv!, this.sendSequenceCounter!);
633 Response = this.protocol.
Decrypt(this.ks_Enc!, IV, EncryptedResponseData);
635 if (IsPadded(Response, out
int NrBytesPadding))
636 Array.Resize(ref Response, Response.Length - NrBytesPadding);
638 Response = CONCAT(Response, [SW1, SW2]);
641 if (this.HasSniffers)
647 private async Task<byte[]> GetRemainingResponseData(
byte[] Response)
649 while (Response.Length >= 2 &&
652 byte Le = Response[^1];
653 byte[] GetResponseCommand =
662 byte[] NextResponse = await this.ExecuteCommandSingle(GetResponseCommand);
663 byte[] CombinedResponse =
new byte[Response.Length + NextResponse.Length - 2];
665 Buffer.BlockCopy(Response, 0, CombinedResponse, 0, Response.Length - 2);
666 Buffer.BlockCopy(NextResponse, 0, CombinedResponse, Response.Length - 2, NextResponse.Length);
667 Response = CombinedResponse;
673 private static bool IsPadded(
byte[] Data, out
int NrBytesPadding)
684 while (c > 0 && Data[--c] == 0)
690 NrBytesPadding = Data.Length - c;
695 private void UnexpectedEndOfResponse()
697 this.Error(
"Unexpected end of encrypted response.");
700 private void IncrementCounter()
702 if (this.sendSequenceCounter is
null)
703 throw new InvalidOperationException(
"Send Sequence Counter is not initialized.");
705 int i = this.sendSequenceCounter.Length;
707 while (++this.sendSequenceCounter[--i] == 0 && i >= 0)
716 private bool CheckResponse(
byte[] CheckResponse)
718 if (CheckResponse is
null || CheckResponse.Length < 2)
721 byte SW1 = CheckResponse[^2];
722 byte SW2 = CheckResponse[^1];
730 this.Information(SW2.ToString(CultureInfo.InvariantCulture) +
" bytes still available");
737 this.Warning(
"Warning, state unchanged. No information given.");
741 this.Warning(
"Warning " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card. State unchanged.");
745 this.Warning(
"Part of returned data may be corrupted");
749 this.Warning(
"End of file or record reached before reading Ne bytes.");
753 this.Warning(
"Selected file deactivated.");
757 this.Warning(
"File control information not formatted correctly.");
761 this.Warning(
"Selected file in termination state.");
765 this.Warning(
"No input data available from a sensor on the card.");
774 this.Warning(
"Warning, state changed. No information given.");
778 this.Warning(
"Warning " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card. State changed.");
782 this.Warning(
"File filled up by the last write.");
791 this.Error(
"Error, state unchanged. No information given.");
795 this.Error(
"Error " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card. State unchanged.");
799 this.Error(
"Immediate response required by the card.");
808 this.Error(
"Error, state changed. No information given.");
812 this.Error(
"Error " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card. State changed.");
816 this.Error(
"Memory failure.");
822 this.Error(
"Security issue detected.");
826 this.Error(
"Wrong length.");
833 this.Error(
"Function Not Supported. No information given.");
837 this.Error(
"Function Not Supported " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card.");
841 this.Error(
"Logical channel not supported.");
845 this.Error(
"Secure messaging not supported.");
849 this.Error(
"Last command of the chain expected.");
853 this.Error(
"Command chaining not supported.");
862 this.Error(
"Not Allowed. No information given.");
866 this.Error(
"Not Allowed " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card.");
870 this.Error(
"Command incompatible with file structure.");
874 this.Error(
"Security status not satisfied.");
878 this.Error(
"Authentication method blocked.");
882 this.Error(
"Reference data not usable.");
886 this.Error(
"Conditions of use not satisfied.");
890 this.Error(
"Command not allowed (no current EF).");
894 this.Error(
"Expected secure messaging data objects missing.");
898 this.Error(
"Incorrect secure messaging data objects.");
907 this.Error(
"Wrong Parameters. No information given.");
911 this.Error(
"Wrong Parameters " + SW2.ToString(
"X2", CultureInfo.InvariantCulture) +
" triggered by card.");
915 this.Error(
"Incorrect parameters in the command data field.");
919 this.Error(
"Function not supported.");
923 this.Error(
"File or application not found.");
927 this.Error(
"Record not found.");
931 this.Error(
"Not enough memory space in the file.");
935 this.Error(
"Nc inconsistent with TLV structure.");
939 this.Error(
"Incorrect parameters P1-P2.");
943 this.Error(
"Nc inconsistent with parameters P1-P2.");
947 this.Error(
"Referenced data or reference data not found (exact meaning depending on the command).");
951 this.Error(
"File already exists.");
955 this.Error(
"DF name already exists.");
961 this.Error(
"Le field incorrect. Should be " + SW2.ToString(
"X2", CultureInfo.InvariantCulture));
965 this.Error(
"Unexpected response received. SW1=" + SW1.ToString(
"X2", CultureInfo.InvariantCulture) +
966 ", SW2=" + SW2.ToString(
"X2", CultureInfo.InvariantCulture));
975 public Task<KeyValuePair<byte[]?, bool>> ReadBinary(uint Offset)
977 return this.ReadBinary(Offset, 0);
985 public async Task<KeyValuePair<byte[]?, bool>> ReadBinary(uint Offset,
byte NrBytes)
991 if (this.HasSniffers)
993 this.Information(
"ReadBinary(" + Offset.ToString(CultureInfo.InvariantCulture) +
"," +
994 NrBytes.ToString(CultureInfo.InvariantCulture) +
")");
999 if (Offset <=
short.MaxValue)
1005 (byte)(Offset >> 8),
1010 else if (Offset < 0x1000000)
1015 ISO_7816.Instructions.ReadBinary + 1,
1021 (byte)(Offset >> 16),
1022 (byte)(Offset >> 8),
1032 ISO_7816.Instructions.ReadBinary + 1,
1038 (byte)(Offset >> 24),
1039 (byte)(Offset >> 16),
1040 (byte)(Offset >> 8),
1046 byte[] Response = await this.ExecuteCommand(Command);
1047 int c = Response.Length;
1049 if (!this.CheckResponse(Response))
1051 if (Response is not
null &&
1056 Response = await this.ExecuteCommand(Command);
1058 if (!this.CheckResponse(Response))
1059 return new KeyValuePair<byte[]?, bool>(
null,
false);
1062 return new KeyValuePair<byte[]?, bool>(
null,
false);
1065 c = Response.Length;
1067 byte[] Data =
new byte[c - 2];
1068 Buffer.BlockCopy(Response, 0, Data, 0, c - 2);
1070 return new KeyValuePair<byte[]?, bool>(Data, More);
1079 public async Task<byte[]?> DownloadFile(ushort FileId,
string FileName)
1083 this.Information(
"Downloading " + FileName +
"...");
1085 if (!await this.SelectFile(FileId))
1088 using MemoryStream File =
new();
1090 int? ExpectedLength =
null;
1091 int BytesDownloaded = 0;
1093 while (!ExpectedLength.HasValue || BytesDownloaded < ExpectedLength.Value)
1095 KeyValuePair<byte[]?, bool> P = await this.ReadBinary(Offset);
1099 File.Write(P.Key, 0, P.Key.Length);
1100 BytesDownloaded += P.Key.Length;
1102 if (!ExpectedLength.HasValue)
1104 ExpectedLength = GetExpectedLength(P.Key);
1105 if (ExpectedLength.HasValue)
1106 this.Information(
"Expected length of file: " + ExpectedLength.Value.ToString(CultureInfo.InvariantCulture));
1109 if (!P.Value && !ExpectedLength.HasValue)
1112 return File.ToArray();
1115 Offset += (uint)P.Key.Length;
1119 return File.ToArray();
1122 private static int? GetExpectedLength(
byte[] Bin)
1128 uint c = (uint)Bin.Length;
1135 if ((b & 0x1f) == 0x1f)
1144 while ((b & 0x80) != 0);
1153 return (
int)(i + b);
1174 if (Length >
int.MaxValue)
1185 private async Task<bool> TryFindPaceProtocol(
object? CardAccess)
1202 if (CardAccess is not Vector SecurityInfos)
1208 foreach (
object Item
in SecurityInfos)
1212 OidsFound.
Add(Current.Oid);
1214 if (this.HasSniffers)
1215 this.Information(
"OID " + Current.Oid +
" (" + Current.GetType().Name.Replace(
'_',
'-') +
") supported.");
1225 else if (Item is Vector SecurityInfo &&
1226 SecurityInfo.Length > 0 &&
1227 SecurityInfo.FirstElement is
string Oid)
1231 if (this.HasSniffers)
1232 this.Information(
"OID " + Oid +
" lacks implemented support.");
1245 Log.
Alert(
"No supported PACE protocol found. OIDs found: " +
1246 string.Join(
", ", OidsFound),
1247 new KeyValuePair<string, object>(
"DocumentType", this.documentInformation.DocumentType ??
string.Empty),
1248 new KeyValuePair<string, object>(
"IssuingState",
this.documentInformation.IssuingState ??
string.Empty),
1249 new KeyValuePair<string, object>(
"Nationality",
this.documentInformation.Nationality ??
string.Empty));
1252 this.protocol = Best;
1253 this.zeroIv =
new byte[this.protocol?.
BlockLength ?? 0];
1255 return Best is not
null;
1262 private async Task<bool> InitializePACE()
1266 if (this.HasSniffers)
1267 this.Information(
"MSE:Set AT(" + this.protocol.
Oid +
",MRZ)");
1269 string[] Parts = this.protocol!.
Oid.Split(
'.');
1270 int i, c = Parts.Length - 1;
1271 byte[] PartBytes =
new byte[c];
1273 for (i = 0; i < c; i++)
1275 if (!
byte.TryParse(Parts[i + 1], out PartBytes[i]))
1279 byte[] ParameterIdEncoding;
1283 ParameterIdEncoding = this.protocol.
ParameterId.Value.ToByteArray(
true,
true);
1285 ParameterIdEncoding = CONCAT(
1288 (
byte)ParameterIdEncoding.Length
1290 ParameterIdEncoding);
1293 ParameterIdEncoding = [];
1301 (
byte)(5 + c + ParameterIdEncoding.Length),
1315 byte[] Response = await this.ExecuteCommand(Command);
1317 return this.CheckResponse(Response);
1326 public static byte[] CONCAT(
byte[] Bytes, params
byte[][] MoreBytes)
1328 int c = Bytes.Length;
1331 foreach (
byte[] A
in MoreBytes)
1334 byte[] Result =
new byte[c];
1336 Buffer.BlockCopy(Bytes, 0, Result, 0, i);
1338 foreach (
byte[] A
in MoreBytes)
1340 Buffer.BlockCopy(A, 0, Result, i, c = A.Length);
1353 public static byte[] XOR(
byte[] A,
byte[] B)
1355 int i, c = A.Length;
1358 throw new ArgumentException(
"Byte arrays must have the same length.");
1360 byte[] Result =
new byte[c];
1361 for (i = 0; i < c; i++)
1362 Result[i] = (
byte)(A[i] ^ B[i]);
1386 public static byte[] KDF(
byte[] KSeed,
int Counter,
bool AdjustParity,
1389 int c = KSeed.Length;
1390 byte[] D =
new byte[c + 4];
1391 Buffer.BlockCopy(KSeed, 0, D, 0, c);
1394 for (i = c + 3; i >= c; i--)
1396 D[i] = (byte)Counter;
1402 if (H.Length > NrBytes)
1403 Array.Resize(ref H, NrBytes);
1411 private static void OddParity(
byte[] H)
1413 int i, j, c = H.Length;
1416 for (i = 0; i < c; i++)
1439 Array.Resize(ref H, 16);
1448 return BAC_KDF(Info, 1,
true);
1456 return BAC_KDF(Info, 2,
true);
1461 byte[] KSeed = BAC_KSeed(Info);
1469 private async Task<byte[]?> GetPaceEncryptedNonce()
1473 this.Information(
"General Authenticate (Get Encrypted Nonce)");
1486 byte[] Response = await this.ExecuteCommand(Command);
1488 if (!this.CheckResponse(Response))
1491 if (Response.Length < 6 ||
1492 Response[0] != 0x7c ||
1493 Response.Length != Response[1] + 4 ||
1494 Response[2] != 0x80 ||
1495 Response.Length != Response[3] + 6 ||
1496 Response[^2] != 0x90 ||
1497 Response[^1] != 0x00)
1499 this.Error(
"Unexpected response received.");
1503 int c = Response[3];
1504 byte[] Nonce =
new byte[c];
1506 Buffer.BlockCopy(Response, 4, Nonce, 0, c);
1516 private async Task<byte[]?> GetPaceRemotePublicKey(
byte[] LocalPublicKey)
1520 return DecodePublicKey(await this.GeneralAuthenticate(
1521 EncodePublicKey(LocalPublicKey),
1522 "Get Remote Public Key",
1533 private async Task<byte[]?> GetPaceRemotePublicEphemeralKey(
byte[] LocalPublicEphemeralKey)
1537 return DecodePublicKey(await this.GeneralAuthenticate(
1538 EncodePublicKey(LocalPublicEphemeralKey),
1539 "Get Remote Ephemeral Public Key",
1550 private async Task<byte[]?> GetPaceRemoteVerificationToken(
1551 byte[] LocalVerificationToken)
1555 return await this.GeneralAuthenticate(LocalVerificationToken,
1556 "Get Remote Verification Token",
1562 private static byte[] EncodePublicKey(
byte[] LocalPublicKey)
1564 int c = LocalPublicKey.Length;
1565 byte[] EncodedPublicKey =
new byte[c + 1];
1567 EncodedPublicKey[0] = 4;
1568 Buffer.BlockCopy(LocalPublicKey, 0, EncodedPublicKey, 1, c);
1570 return EncodedPublicKey;
1573 private static byte[]? DecodePublicKey(
byte[]? Data)
1577 if (Data is
null || (c = Data.Length) == 0 || Data[0] != 4)
1580 byte[] DecodedPublicKey =
new byte[c - 1];
1581 Buffer.BlockCopy(Data, 1, DecodedPublicKey, 0, c - 1);
1583 return DecodedPublicKey;
1586 private async Task<byte[]?> GeneralAuthenticate(
byte[] Data,
string Comment,
bool LastInChain,
1587 byte Command,
byte ExpectedResponse)
1589 this.Information(
"General Authenticate (" + Comment +
")");
1591 int c = Data.Length;
1593 byte[] Request = CONCAT(
1612 byte[] Response = await this.ExecuteCommand(Request);
1614 if (!this.CheckResponse(Response))
1617 if (Response.Length < 6 ||
1618 Response[0] != 0x7c ||
1619 Response.Length != Response[1] + 4 ||
1620 Response[2] != ExpectedResponse ||
1621 Response.Length != Response[3] + 6 ||
1622 Response[^2] != 0x90 ||
1623 Response[^1] != 0x00)
1625 this.Error(
"Unexpected response received.");
1630 byte[] ResponseData =
new byte[c];
1632 Buffer.BlockCopy(Response, 4, ResponseData, 0, c);
1634 return ResponseData;
1641 private async Task<byte[]?> GetBacChallenge()
1645 this.Information(
"GetChallenge");
1656 byte[] Response = await this.ExecuteCommand(Command);
1658 if (!this.CheckResponse(Response))
1661 if (Response.Length != 10 || Response[8] != 0x90 || Response[9] != 0x00)
1663 this.Error(
"Unexpected response received.");
1667 byte[] Challenge =
new byte[8];
1668 Buffer.BlockCopy(Response, 0, Challenge, 0, 8);
1678 private async Task<byte[]?> ExternalBacAuthenticate(
byte[] ChallengeResponse)
1682 this.Information(
"ChallengeResponse");
1684 byte Lc = (byte)ChallengeResponse.Length;
1685 byte[] Command = CONCAT(
1698 byte[] Response = await this.ExecuteCommand(Command);
1700 if (!this.CheckResponse(Response))
1703 if (Response.Length != 10 || Response[8] != 0x90 || Response[9] != 0x00)
1705 this.Error(
"Unexpected response received.");
1709 byte[] Challenge =
new byte[8];
1710 Buffer.BlockCopy(Response, 0, Challenge, 0, 8);
1720 public async Task<AuthenticateResult> Authenticate()
1724 byte[]? Data = await this.TryDownloadCardAccessForAuthentication();
1726 if (Data is not
null &&
1728 await
this.TryFindPaceProtocol(CardAccess))
1736 if (this.HasSniffers)
1737 this.Information(
"PACE protocol " + this.protocol!.GetType().Name.Replace(
'_',
'-') +
" selected.");
1739 if (!await this.InitializePACE())
1741 this.Error(
"Unable to initialize PACE protocol.");
1745 this.Information(
"PACE protocol initialized (" + EecProtocol.Curve?.CurveName +
").");
1747 this.Information(
"PACE protocol initialized.");
1749 if (!await this.protocol!.Authenticate(
this))
1751 this.Error(
"Authentication unsuccessful.");
1760 this.Information(
"Attempting legacy BAC protocol.");
1764 byte[]? Challenge = await this.GetBacChallenge();
1766 if (Challenge is
null)
1768 this.Error(
"Unable to get BAC challenge.");
1772 byte[] ChallengeResponse = CalcChallengeResponse3DES(this.documentInformation, Challenge);
1773 byte[]? Response = await this.ExternalBacAuthenticate(ChallengeResponse);
1783 private async Task<byte[]?> TryDownloadCardAccessForAuthentication()
1785 byte[]? Data = await this.DownloadFile(
EF.
CardAccess,
"EF.CardAccess");
1786 if (Data is not
null)
1792 this.Information(
"Retrying EF.CardAccess after explicit master file selection.");
1793 if (!await this.SelectMaster())
1795 this.Error(
"Unable to select the master file before reading EF.CardAccess.");
1799 Data = await this.DownloadFile(
EF.
CardAccess,
"EF.CardAccess");
1812 public static byte[] CalcChallengeResponse3DES(
byte[] Challenge,
byte[] Rnd1,
byte[] Rnd2,
1813 byte[] KEnc,
byte[] KMac)
1815 byte[] S = CONCAT(Rnd1, Challenge, Rnd2);
1819 using (TripleDES Cipher = TripleDES.Create())
1821 Cipher.Mode = CipherMode.CBC;
1822 Cipher.Padding = PaddingMode.None;
1824 using ICryptoTransform Encryptor = Cipher.CreateEncryptor(KEnc,
new byte[8]);
1825 EIFD = Encryptor.TransformFinalBlock(S, 0, 32);
1831 using (DES Cipher = DES.Create())
1833 Cipher.Mode = CipherMode.CBC;
1834 Cipher.Padding = PaddingMode.None;
1837 int c = EIFD.Length;
1840 byte[] Data =
new byte[c + 8];
1841 Buffer.BlockCopy(EIFD, 0, Data, 0, c);
1844 byte[] Ka =
new byte[8];
1845 byte[] Kb =
new byte[8];
1847 Buffer.BlockCopy(KMac, 0, Ka, 0, 8);
1848 Buffer.BlockCopy(KMac, 8, Kb, 0, 8);
1850 byte[] Block =
new byte[8];
1854 using (ICryptoTransform Encryptor2 = Cipher.CreateEncryptor(Ka,
new byte[8]))
1858 Buffer.BlockCopy(Data, i, Block, 0, 8);
1863 for (j = 0; j < 8; j++)
1867 H = Encryptor2.TransformFinalBlock(Block, 0, 8);
1870 using (ICryptoTransform FinalDecryptor = Cipher.CreateDecryptor(Kb,
new byte[8]))
1872 H = FinalDecryptor.TransformFinalBlock(H!, 0, 8);
1875 H = Encryptor2.TransformFinalBlock(H, 0, 8);
1881 return CONCAT(EIFD, MIFD);
1890 public static byte[] CalcChallengeResponse3DES(
DocumentInformation Info,
byte[] Challenge)
1892 byte[] Rnd1 =
new byte[8];
1893 byte[] Rnd2 =
new byte[16];
1895 using (RandomNumberGenerator Rnd = RandomNumberGenerator.Create())
1901 return CalcChallengeResponse3DES(Challenge, Rnd1, Rnd2, BAC_KEnc(Info), BAC_KMac(Info));
1909 internal async Task<bool> AuthenticateGenericMapping()
1922 byte[]? z = await this.GetPaceEncryptedNonce();
1925 this.Error(
"Unable to get PACE encrypted nonce.");
1931 byte[] Kπ = this.protocol.
KDFπ(this.documentInformation);
1938 byte[] LocalPublicKey;
1944 LocalPublicKey = this.protocol.
CreateNewKey(this.localKeySeed, ref KeyIndex);
1946 if (Replay is not
null)
1948 string LocalPrivateKey = Replay.GetInfo(
"Local private key:",
this);
1949 XmlDocument Doc =
new();
1950 Doc.LoadXml(LocalPrivateKey);
1952 byte[] LocalPublicKey2 = this.protocol.
ImportKey(Doc);
1954 Curve = EcdhProtocol.Curve;
1958 if (this.localKeySeed is
null)
1959 LocalPublicKey = LocalPublicKey2;
1960 else if (Convert.ToBase64String(LocalPublicKey) != Convert.ToBase64String(LocalPublicKey2))
1962 this.Error(
"Local public key mismatch.");
1968 this.Information(
"Local private key: " + Curve.
Export());
1970 byte[]? RemotePublicKey = await this.GetPaceRemotePublicKey(LocalPublicKey);
1972 if (RemotePublicKey is
null)
1974 this.Error(
"Unable to get PACE remote public key.");
1980 if (!Curve.
IsPoint(RemotePublicKey,
true))
1982 this.Error(
"Remote public key not on curve.");
1994 PointOnCurve Ĝ = EcdhProtocol.GetGenericMap(s, RemotePublicKey);
1995 byte[] Generator = Curve.
Encode(Ĝ,
true);
2001 byte[] LocalEphemeralPrivateKey;
2003 if (Replay is not
null)
2005 string EphemeralKey = Replay.GetInfo(
"Local ephemeral private key:",
this);
2008 if (this.localKeySeed is not
null)
2010 byte[] LocalEphemeralPrivateKey2 = EcdhProtocol.GenerateSecret(this.localKeySeed, ref KeyIndex);
2012 if (Convert.ToBase64String(LocalEphemeralPrivateKey) != Convert.ToBase64String(LocalEphemeralPrivateKey2))
2014 this.Error(
"Local ephemeral private key mismatch.");
2020 LocalEphemeralPrivateKey = EcdhProtocol.GenerateSecret(this.localKeySeed, ref KeyIndex);
2022 this.Information(
"Local ephemeral private key: " +
Hashes.
BinaryToString(LocalEphemeralPrivateKey));
2025 byte[] LocalEphemeralPublicKey = Curve.
Encode(P1,
true);
2027 this.Information(
"Local ephemeral public key: " +
Hashes.
BinaryToString(LocalEphemeralPublicKey));
2029 byte[]? RemoteEphemeralPublicKey = await this.GetPaceRemotePublicEphemeralKey(LocalEphemeralPublicKey);
2031 if (RemoteEphemeralPublicKey is
null)
2033 this.Error(
"Unable to get PACE remote ephemeral public key.");
2037 this.Information(
"Remote ephemeral public key: " +
Hashes.
BinaryToString(RemoteEphemeralPublicKey));
2039 if (!Curve.
IsPoint(RemoteEphemeralPublicKey,
true))
2041 this.Error(
"Remote ephemeral public key not on curve.");
2047 int c = RemoteEphemeralPublicKey.Length;
2049 byte[] RemoteEphemeralPublicKeyX =
new byte[c2];
2050 byte[] RemoteEphemeralPublicKeyY =
new byte[c2];
2052 Buffer.BlockCopy(RemoteEphemeralPublicKey, 0, RemoteEphemeralPublicKeyX, 0, c2);
2053 Buffer.BlockCopy(RemoteEphemeralPublicKey, c2, RemoteEphemeralPublicKeyY, 0, c2);
2055 Array.Reverse(RemoteEphemeralPublicKeyX);
2056 Array.Reverse(RemoteEphemeralPublicKeyY);
2063 LocalEphemeralPrivateKey, RemoteEphemeralPublicPoint,
true);
2065 byte[] EphemeralSharedPointX = EphemeralSharedPoint.
X.ToByteArray();
2067 if (EphemeralSharedPointX.Length != Curve.
OrderBytes)
2068 Array.Resize(ref EphemeralSharedPointX, Curve.
OrderBytes);
2070 Array.Reverse(EphemeralSharedPointX);
2076 this.ks_Enc = this.protocol.
KDF_Enc(EphemeralSharedPointX);
2077 this.ks_Mac = this.protocol.
KDF_Mac(EphemeralSharedPointX);
2084 byte[] AD_IFD = PaceProtocol.CreateAssociatedData(this.protocol.
Oid, RemoteEphemeralPublicKey);
2085 byte[] AD_IC = PaceProtocol.CreateAssociatedData(this.protocol.
Oid, LocalEphemeralPublicKey);
2094 byte[] T_IFD = this.cMac.
Sign(AD_IFD, 8);
2098 byte[]? RemoteToken = await this.GetPaceRemoteVerificationToken(T_IFD);
2100 if (RemoteToken is
null)
2102 this.Error(
"Unable to get remote token.");
2108 if (!this.cMac.
Verify(AD_IC, RemoteToken))
2110 byte[] T_IC = this.cMac.
Sign(AD_IC, 8);
2116 this.Information(
"Authentication successful.");
2118 this.encrypted =
true;
2119 this.enhancedSecurity =
false;
2120 this.sendSequenceCounter =
new byte[this.protocol.
BlockLength];
2124 catch (Exception ex)
2134 public bool ReadDG1 {
get;
set; } =
true;
2139 public bool ReadDG2 {
get;
set; } =
true;
2144 public bool ReadDG3 {
get;
set; } =
false;
2149 public bool ReadDG4 {
get;
set; } =
false;
2154 public bool ReadDG5 {
get;
set; } =
false;
2159 public bool ReadDG7 {
get;
set; } =
false;
2164 public bool ReadDG8 {
get;
set; } =
false;
2169 public bool ReadDG9 {
get;
set; } =
false;
2174 public bool ReadDG10 {
get;
set; } =
false;
2179 public bool ReadDG11 {
get;
set; } =
true;
2184 public bool ReadDG12 {
get;
set; } =
false;
2189 public bool ReadDG13 {
get;
set; } =
false;
2194 public bool ReadDG14 {
get;
set; } =
false;
2199 public bool ReadDG15 {
get;
set; } =
false;
2204 public bool ReadDG16 {
get;
set; } =
false;
2211 public async Task<ReadTravelDocumentResult> ReadTravelDocument(
string IdDomain)
2215 this.Error(
"Unable to select the LDS1 eMRTD application.");
2221 this.Information(
"LDS1 eMRTD application selected.");
2223 byte[]? Data = await this.DownloadFile(
EF.
COM,
"EF.COM");
2226 this.Error(
"Unable to download EF.COM.");
2232 this.Error(
"Unable to parse application level information.");
2236 this.appInfo = AppInfo;
2237 await this.AppInfoUpdated.Raise(
this, EventArgs.Empty);
2241 Data = await this.DownloadFile(
EF.
SOD,
"EF.SOD");
2244 this.Error(
"Unable to download EF.SOD.");
2250 this.Error(
"Unable to decode Document Security Object.\r\n\r\n" +
2251 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2255 if ((SecurityInfo.SignedData?.Certificates?.Length ?? 0) == 0)
2257 this.Error(
"No certificates available in EF.SOD.");
2261 if (SecurityInfo.SignedData!.Certificates.Length > 1)
2263 this.Error(
"Multiple certificates available in EF.SOD.");
2269 this.Information(
"Validating certificate.");
2272 foreach (
Certificate Cert
in SecurityInfo.SignedData!.Certificates)
2275 Dictionary<string, bool> CrlUrls = [];
2277 foreach (
string CrlUrl
in GetRevocationListUrls(Cert))
2278 CrlUrls[CrlUrl] =
true;
2280 KeyValuePair<string?, byte[]?> P = GetAuthorityKeyIdentifier(Cert);
2281 Dictionary<string, bool> Processed = [];
2283 byte[]? IssuerKeyReference = P.Value;
2285 if (
string.IsNullOrEmpty(CountryCode) || IssuerKeyReference is
null)
2287 this.Error(
"Required Authority Key Identifier not found in certificate.");
2291 while (!
string.IsNullOrEmpty(CountryCode) && IssuerKeyReference is not
null)
2293 string Key = Convert.ToBase64String(IssuerKeyReference);
2294 if (Processed.ContainsKey(Key))
2297 Processed[Key] =
true;
2299 this.Information(
"Retrieving issuer certificate: " +
Hashes.
BinaryToString(IssuerKeyReference));
2302 IdDomain, CountryCode, IssuerKeyReference,
this);
2304 if (IssuerCertificate is
null)
2306 this.Error(
"Issuer certificate not found.");
2310 Certificates.
Insert(0, IssuerCertificate);
2314 foreach (
string CrlUrl
in GetRevocationListUrls(IssuerCertificate))
2315 CrlUrls[CrlUrl] =
true;
2317 P = GetAuthorityKeyIdentifier(IssuerCertificate);
2319 IssuerKeyReference = P.Value;
2322 if (CrlUrls.Count == 0)
2324 this.Error(
"No approved CRLs found.");
2328 foreach (
string CrlUrl
in CrlUrls.Keys)
2330 this.Information(
"Retrieving CRL: " + CrlUrl);
2333 if (RevokedCertificates is
null)
2335 this.Error(
"Unable to load CRL.");
2339 this.Information(
"Verifying CRL signature.");
2341 if (!await RevokedCertificates.
VerifySignature(IdDomain, CountryCode!,
this))
2343 this.Error(
"CRL Signature invalid.");
2347 this.Information(
"Checking if certificates are revoked.");
2351 this.Error(
"Certificate " + Cert.
SerialNumber.ToString(
"X", CultureInfo.InvariantCulture) +
" has been revoked: " +
Reason.ToString());
2355 foreach (
Certificate Certificate2
in Certificates)
2357 if (RevokedCertificates.
HasBeenRevoked(Certificate2, out Reason))
2359 this.Error(
"Certificate " + Certificate2.
SerialNumber.ToString(
"X", CultureInfo.InvariantCulture) +
" has been revoked: " +
Reason.ToString());
2365 this.Information(
"Verifying certificate chain.");
2369 this.Error(
"Signatures in certificate chain not valid.");
2374 this.securityinfo = SecurityInfo;
2375 await this.SecurityInfoUpdated.Raise(
this, EventArgs.Empty);
2381 this.Information(
"EF.DG1 (MRZ) supported.");
2383 Data = await this.DownloadFile(
EF.
DG1,
"EF.DG1");
2386 this.Error(
"Unable to download EF.DG1.");
2390 if (!this.ValidateDataGroupData(1, Data))
2394 DataGroup1.Mrz is
null)
2396 this.Error(
"Unable to decode DG1 (MRZ Information).\r\n\r\n" +
2397 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2401 this.mrz = DataGroup1.Mrz;
2402 if (this.mrz.DocumentInformation is
null)
2403 this.Warning(
"Unable to parse MRZ information.");
2405 await this.MrzUpdated.Raise(
this, EventArgs.Empty);
2412 this.Information(
"EF.DG2 (Encoded Identification Features — Face) supported.");
2414 Data = await this.DownloadFile(
EF.
DG2,
"EF.DG2");
2417 this.Error(
"Unable to download EF.DG2.");
2421 if (!this.ValidateDataGroupData(2, Data))
2426 this.Error(
"Unable to decode Biometric Encoding in DG2 (Encoded Identification Features — Face).\r\n\r\n" +
2427 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2431 this.biometricEncodingFace = BiometricEncoding.Templates?.Templates;
2432 await this.BiometricEncodingFaceUpdated.Raise(
this, EventArgs.Empty);
2435 if (this.enhancedSecurity && this.ReadDG3 && (this.appInfo.
TagList?.
HasDataGroup(3) ??
false))
2441 this.Information(
"EF.DG3 (Additional Identification Feature — Finger(s)) supported.");
2443 Data = await this.DownloadFile(
EF.
DG3,
"EF.DG3");
2446 this.Error(
"Unable to download EF.DG3.");
2450 if (!this.ValidateDataGroupData(3, Data))
2455 this.Error(
"Unable to decode Biometric Encoding in DG3 (Additional Identification Feature — Finger(s)).\r\n\r\n" +
2456 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2460 this.biometricEncodingFingers = BiometricEncoding.Templates?.Templates;
2461 await this.BiometricEncodingFingersUpdated.Raise(
this, EventArgs.Empty);
2463 catch (Exception ex)
2465 this.Error(ex.Message);
2469 if (this.enhancedSecurity && this.ReadDG4 && (this.appInfo.
TagList?.
HasDataGroup(4) ??
false))
2475 this.Information(
"EF.DG4 (Additional Identification Feature — Iris(es)) supported.");
2477 Data = await this.DownloadFile(
EF.
DG4,
"EF.DG4");
2480 this.Error(
"Unable to download EF.DG4.");
2484 if (!this.ValidateDataGroupData(4, Data))
2489 this.Error(
"Unable to decode Biometric Encoding in DG4 (Additional Identification Feature — Iris(es)).\r\n\r\n" +
2490 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2494 this.biometricEncodingIrises = BiometricEncoding.Templates?.Templates;
2495 await this.BiometricEncodingIrisesUpdated.Raise(
this, EventArgs.Empty);
2497 catch (Exception ex)
2499 this.Error(ex.Message);
2507 this.Information(
"EF.DG5 (Displayed Portrait) supported.");
2509 Data = await this.DownloadFile(
EF.
DG5,
"EF.DG5");
2512 this.Error(
"Unable to download EF.DG5.");
2516 if (!this.ValidateDataGroupData(5, Data))
2521 this.Error(
"Unable to decode DG5 (Displayed Portrait).\r\n\r\n" +
2522 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2526 if ((DataGroup5?.Photos?.Length ?? 0) > 0)
2529 this.Warning(Convert.ToBase64String(
Photo.Value));
2537 this.Information(
"EF.DG7 (Displayed Signature or Usual Mark) supported.");
2539 Data = await this.DownloadFile(
EF.
DG7,
"EF.DG7");
2542 this.Error(
"Unable to download EF.DG7.");
2546 if (!this.ValidateDataGroupData(7, Data))
2551 this.Error(
"Unable to decode Displayed Signatures in DG7 (Displayed Signature or Usual Mark).\r\n\r\n" +
2552 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2557 await this.DisplayedSignaturesUpdated.Raise(
this, EventArgs.Empty);
2562 this.Warning(
"EF.DG8 (Data Feature(s)) supported but not implemented.");
2569 this.Warning(
"EF.DG9 (Structure Feature(s)) supported but not implemented.");
2576 this.Warning(
"EF.DG10 (Substance Feature(s)) supported but not implemented.");
2585 this.Information(
"EF.DG11 (Additional Personal Detail(s)) supported.");
2587 Data = await this.DownloadFile(
EF.
DG11,
"EF.DG11");
2590 this.Error(
"Unable to download EF.DG11.");
2594 if (!this.ValidateDataGroupData(11, Data))
2599 this.Error(
"Unable to decode DG11 (Additional Personal Detail(s)).\r\n\r\n" +
2600 Convert.ToBase64String(Data, Base64FormattingOptions.InsertLineBreaks));
2605 await this.PersonalInformationUpdated.Raise(
this, EventArgs.Empty);
2610 this.Warning(
"EF.DG12 (Additional Document Detail(s)) supported but not implemented.");
2617 this.Warning(
"EF.DG13 (Optional Details(s)) supported but not implemented.");
2624 this.Warning(
"EF.DG14 (Security Options) supported.");
2631 this.Warning(
"EF.DG15 (Active Authentication Public Key Info) supported but not implemented.");
2638 this.Warning(
"EF.DG16 (Person(s) to Notify) supported but not implemented.");
2648 private bool ValidateDataGroupData(
int Nr,
byte[] Data)
2650 this.Information(
"Validating data with EF.SOD");
2652 if (this.securityinfo is
null)
2654 this.Error(
"EF.SOD not read.");
2659 this.Information(
"Data valid in accordance to Hash Digest in EF.SOD.");
2664 this.Error(
"Invalid data. Hash Digest of data does not match Hash Digest in EF.SOD.");
2676 public static bool TryParseDataObject<T>(
byte[] Data, TravelDocumentsClient Client,
2677 [NotNullWhen(
true)] out T? DataObject)
2680 if (TryParseDataObjects(Data, Client, out
IDataObject[]? DataObjects))
2684 if (Object is T TypedObject)
2686 DataObject = TypedObject;
2692 DataObject =
default;
2703 public static bool TryParseDataObjects(
byte[] Data, TravelDocumentsClient Client,
2704 [NotNullWhen(
true)] out
IDataObject[]? DataObjects)
2710 int c = Data.Length;
2723 for (j = i; j < c; j++)
2733 if ((Tag & 31) == 31)
2773 Value =
new byte[Len];
2776 Buffer.BlockCopy(Data, i, Value, 0, Len);
2780 if (dataObjects.TryGetValue(Tag, out
IDataObject? TypedObject))
2782 if (TypedObject.TryParse(Value, Client, out
IDataObject? ParsedObject))
2783 Found.
Add(ParsedObject);
2786 Client.Warning(
"Unable to parse data object with tag: " + Tag.ToString(
"X4", CultureInfo.InvariantCulture));
2792 Client.Warning(
"Unknown application level information tag: " + Tag.ToString(
"X4", CultureInfo.InvariantCulture));
2797 DataObjects = [.. Found];
2802 private static Dictionary<ushort, IDataObject> GetDataObjects()
2804 Dictionary<ushort, IDataObject> Result = [];
2815 Result[DO.
Tag] = DO;
2817 catch (Exception ex)
2841 return new KeyValuePair<string?, byte[]?>(CountryCode,
2846 return new KeyValuePair<string?, byte[]?>(
null,
null);
2865 return new KeyValuePair<string?, byte[]?>(CountryCode,
2870 return new KeyValuePair<string?, byte[]?>(
null,
null);
2902 if (Urls.
Count == 0)
2904 Client?.
Warning(
"No CRL distribution points found in certificate.");
Class implementing the IIsoDepInterface interface for serial communication with an NFC chip,...
Static class for parsing and decoding security objects encoded using Abstract Syntax Notation 1 (ASN....
static bool TryDecodeDer(byte[] Data, out object? Value)
Decodes a DER-encoded object.
Travel Document Applications.
static readonly byte[] DF1
LDS1 eMRTD Application. §4 ICAO Doc 9303-10: https://www2023.icao.int/publications/Documents/9303_p10...
Static class for validation of ICAO certificate chains
static bool VerifySignatures(params Certificate[] Certificates)
Verifies the signatures of a chain of ICAO certificates.
Certificate, as defined in RFC 5280, §4.1.
System.Numerics.BigInteger SerialNumber
Serial Number
Vector? Extensions
Extensions
Internal store of ICAO certificates
static Task< Certificate?> TryLoadCertificate(string IdDomain, string Country, byte[] KeyReference)
Tries to load an ICAO certificate, provided its country and key reference.
static Task< CertificateList?> TryLoadCrl(string Url)
Tries to load an ICAO-compliant CRL, provided its URL.
Additional Personal Details. Reference: §4.7.11, EF.DG11, ICAO Doc 9303-10, Table 71.
Biometric Encoding of DG2. Reference: §4.7.2, EF.DG2, ICAO Doc 9303-10, Table 43.
Biometric Encoding of DG3. Reference: §4.7.3, EF.DG3, ICAO Doc 9303-10, Table 46.
Biometric Encoding of DG4. Reference: §4.7.4, EF.DG4, ICAO Doc 9303-10, Table 53.
Displayed Portraits in DG5. Reference: §4.7.5, EF.DG5, ICAO Doc 9303-10, Table 58.
Displayed Signatures or Usual Marks (DG7). Reference: §4.7.7, EF.DG7, ICAO Doc 9303-10,...
Document Security Object. Reference: §4.6.2, EF.SOD, ICAO Doc 9303-10, Table 36.
bool ValidateDataGroup(int Nr, byte[] DataRead)
Validates data read from a data group, using the information in the LDS Security Object.
bool HasDataGroup(int DataGroupNumber)
Checks if a data group is supported.
Elementary Files in travel documents.
const ushort DG4
Data Group 4 (Additional Identification Feature — Iris(es)) (In LDS1 eMRTD Application)
const ushort DG1
Data Group 1 (MRZ) (In LDS1 eMRTD Application)
const ushort SOD
Security Object Data (In LDS1 eMRTD Application)
const ushort DG3
Data Group 3 (Additional Identification Feature — Finger(s)) (In LDS1 eMRTD Application)
const ushort DG7
Data Group 7 (Displayed Signature or Usual Mark) (In LDS1 eMRTD Application)
const ushort COM
Common Data (In LDS1 eMRTD Application)
const ushort DG5
Data Group 5 (Displayed Portrait) (In LDS1 eMRTD Application)
const ushort CardAccess
EF.CardAccess. §3.11.3 ICAO Doc 9303-10: https://www2023.icao.int/publications/Documents/9303_p10_con...
const ushort DG11
Data Group 11 (Additional Personal Detail(s)) (In LDS1 eMRTD Application)
const ushort DG2
Data Group 2 (Encoded Identification Features — Face) (In LDS1 eMRTD Application)
Event arguments for travel document state-related events.
const byte SecureMessaging
Secure messaging.
const byte Basic
Standard Command, Basic Channel, No Secure Messaging.
const byte Chaining
Command Chaining.
Static class with extensions related to the ISO/IEC 7816 standard for Identification cards — Integrat...
Implements the CMAC algorithm, as defined in NIST SP 800-38B, revision 2016. Ref: https://nvlpubs....
bool Verify(byte[] Message, byte[] Signature)
Verifies a CMAC Signature.
byte[] Sign(byte[] Message, int Len)
Signs a message using the current CMAC.
Certificate List, as defined in RFC 5280, §5.1
bool HasBeenRevoked(Certificate Certificate, out RevokedReason Reason)
Checks if a certificate has been revoked.
Task< bool > VerifySignature(string IdDomain, string CountryCode)
Verifies the signature of the CRL
string Url
URL to CRL distribution point.
DistributionPoint[] Points
Distribution points.
byte[] Identifier
Key identifier
Static class managing encoding and decoding of internet content.
static readonly Encoding ISO_8859_1
ISO-8859-1 character encoding.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Alert(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an alert event.
Simple base class for classes implementing communication protocols.
A chunked list is a linked list of chunks of objects of type T .
bool HasFirstItem
If there is a first item in the collection
void Insert(int Index, T Item)
Inserts an item to the list at the specified index.
int Count
Number of elements in collection.
void Add(T Item)
Adds an item to the collection.
Static class that dynamically manages types and interfaces available in the runtime environment.
static object[] NoParameters
Contains an empty array of parameter values.
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Abstract base class for elliptic curves.
byte[] Encode(PointOnCurve Point)
Encodes a point on the curve.
virtual bool IsPoint(byte[] Point)
Checks if an encoded point is on the curve.
virtual void Export(XmlWriter Output)
Exports the curve parameters to XML.
static BigInteger ToInt(byte[] Binary, bool BigEndian)
Converts a little-endian binary representation of a big integer to a BigInteger.
int OrderBytes
Number of bytes required to represent the order of the curve.
PointOnCurve ScalarMultiplication(BigInteger N, PointOnCurve P, bool Normalize)
Performs the scalar multiplication of N *P .
Contains methods for simple hash calculations.
static byte[] ComputeSHA1Hash(byte[] Data)
Computes the SHA-1 hash of a block of binary data.
static byte[] StringToBinary(string s)
Parses a hex string.
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
ISO DEP interface, for communication with an NFC Tag.
Basic interface for PACE protocols.
byte[] DecryptNonce(byte[] Kπ, byte[] EncryptedNonce)
Decrypts an encrypted nonce value.
byte[] KDFπ(DocumentInformation DocInfo)
Calculates Kπ, given the shared secret and the document information.
byte[] Decrypt(byte[] KS_Enc, byte[] IV, byte[] Data)
Decrypts data.
byte[] KDF_Enc(byte[] KSeed)
KDF_Enc
int BlockLength
Number of bytes used for blocks.
CMac GetAuthenticator(byte[] Key)
Gets the authenticator
System.Numerics.? BigInteger ParameterId
Optional Parameter ID.
byte[] CreateNewKey(byte[]? Seed, ref int Index)
Creates a new private and public key, used in the PACE protocol.
Grade SecurityStrength
Security strength mapped as a grade.
byte[] KDF_Mac(byte[] KSeed)
KDF_Mac
byte[] Encrypt(byte[] KS_Enc, byte[] IV, byte[] Data)
Encrypts data.
byte[] ImportKey(XmlDocument Xml)
Imports the private key and public key from a previous export.
bool ChipAuthenticationMapping
If Chip-Authentication-Mapping is supported by the protocol.
byte[] GetSharedSecret(byte[] RemotePublicKey)
Gets the shared secret, given the local private key previously generated using CreateNewKey and a rem...
string Oid
OID identifying the object.
void Warning(string Warning)
Called to inform the viewer of a warning state.
Interface for observable classes implementing communication protocols.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
class CountryCode(ScriptNode Argument, int Start, int Length, Expression Expression)
Looks up a Country Name and returns the corresponding Country Code.
class BinaryDataObject(ushort Tag, byte[] Value)
Binary Data Object. Used when no specific data object type is defined for a given tag.
abstract class PaceEcdhProtocol()
Abstract base class for PACE protocols using Elliptic Curve Cryptography (EEC).
RevokedReason
Reason for revoking a certificate
ReadTravelDocumentResult
Enumerations of possible results when reading a travel document.
AuthenticateResult
Enumerations of possible results when authenticating the app with the travel document.
Iso7816StatusCategory
First byte of a status word.
TravelDocumentsState
State of travel documents interface.
class Photo(byte[] Binary, int Rotation, Attachment? Attachment)
Class containing information about a photo.
class Header(ISimulationNode Parent, Model Model)
Represents an identity property.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
Reason
Reason a token is not valid.
delegate byte[] HashFunctionArray(byte[] Data)
Delegate to hash function.
HashFunction
Hash method enumeration.
Represents a point on a curve.