libs.nfc/NFC/APDUCommand.cs

150 lines
5.7 KiB
C#
Raw Permalink Normal View History

2021-03-31 14:11:57 +02:00
using NFC.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
2021-03-30 22:51:02 +02:00
namespace NFC
{
2021-03-31 14:11:57 +02:00
/// <summary>
/// Application Protocol Data Unit
/// https://de.wikipedia.org/wiki/Application_Protocol_Data_Unit
/// https://github.com/danm-de/pcsc-sharp/blob/246fc4303190184d6acd98a2d66f48cb7ffd7094/src/PCSC.Iso7816/CommandApdu.cs
/// </summary>
2021-03-30 22:51:02 +02:00
public class APDUCommand
{
2021-03-31 14:11:57 +02:00
#region Constructors
public APDUCommand(IsoCase isoCase, SCardProtocol sCardProtocol = SCardProtocol.ANY)
2021-03-30 22:51:02 +02:00
{
2021-03-30 23:27:12 +02:00
Case = isoCase;
2021-03-31 14:11:57 +02:00
Protocol = sCardProtocol;
Data = new byte[0];
2021-03-30 22:51:02 +02:00
}
2021-03-31 14:11:57 +02:00
#endregion
2021-03-30 22:51:02 +02:00
2021-03-31 14:11:57 +02:00
#region Properties
2021-03-30 23:27:12 +02:00
public IsoCase Case { get; set; }
2021-03-31 14:11:57 +02:00
public SCardProtocol Protocol { get; set; }
public byte CLA { get; set; } = 0x00;
public byte INS { get; set; } = 0x00;
public byte P1 { get; set; } = 0x00;
public byte P2 { get; set; } = 0x00;
public byte LC
{
get
{
return (byte)Data.Length;
}
}
2021-03-30 23:27:12 +02:00
public byte[] Data { get; set; }
2021-03-31 14:11:57 +02:00
public byte LE { get; set; } = 0x00;
#endregion
2021-03-30 22:51:02 +02:00
2021-03-31 14:11:57 +02:00
#region Methods
2021-03-30 23:27:12 +02:00
public byte[] ToArray()
2021-03-30 22:51:02 +02:00
{
2021-03-31 14:11:57 +02:00
byte[] header = ByteOperation.Concatenate(new byte[] { CLA }, new byte[] { INS }, new byte[] { P1 }, new byte[] { P2 });
switch (Case)
{
case IsoCase.Case1:
/* Regarding to OpenSC: T0 needs one additional
* byte containing 0x00. */
if (Protocol == SCardProtocol.T0)
{
return ByteOperation.Concatenate(header, new byte[] { 0x00 });
}
else
{
return header;
}
case IsoCase.Case2Short:
return ByteOperation.Concatenate(header, new byte[] { LE });
case IsoCase.Case3Short:
return ByteOperation.Concatenate(header, new byte[] { LC }, Data);
case IsoCase.Case4Short:
/* Regarding to OpenSC: T0 has no Le */
if (Protocol == SCardProtocol.T0)
{
return ByteOperation.Concatenate(header, new byte[] { LC }, Data, new byte[] { 0x00 });
}
else
{
return ByteOperation.Concatenate(header, new byte[] { LC }, Data, new byte[] { LE });
}
default:
throw new NotSupportedException(string.Format("IsoCase {0} is not supported.", Case));
}
}
public override bool Equals(object obj)
{
return obj is APDUCommand command &&
Case == command.Case &&
Protocol == command.Protocol &&
CLA == command.CLA &&
INS == command.INS &&
P1 == command.P1 &&
P2 == command.P2 &&
Data.SequenceEqual(command.Data) &&
LE == command.LE;
}
public override int GetHashCode()
{
int hashCode = -98047210;
hashCode = hashCode * -1521134295 + Case.GetHashCode();
hashCode = hashCode * -1521134295 + Protocol.GetHashCode();
hashCode = hashCode * -1521134295 + CLA.GetHashCode();
hashCode = hashCode * -1521134295 + INS.GetHashCode();
hashCode = hashCode * -1521134295 + P1.GetHashCode();
hashCode = hashCode * -1521134295 + P2.GetHashCode();
hashCode = hashCode * -1521134295 + LC.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<byte[]>.Default.GetHashCode(Data);
hashCode = hashCode * -1521134295 + LE.GetHashCode();
return hashCode;
}
public override string ToString()
{
string pattern_case1 = "(CASE: 1) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x}";
string pattern_case2 = "(CASE: 2) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x} | LE: 0x{4:x} |";
string pattern_case3 = "(CASE: 3) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x} | LC: 0x{4:x} | Data: {5:x}";
string pattern_case4 = "(CASE: 4) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x} | LC: 0x{4:x} | Data: {5:x} | LE: 0x{6:x} |";
switch (Case)
{
case IsoCase.Case1:
return string.Format(pattern_case1, CLA, INS, P1, P2);
case IsoCase.Case2Short:
case IsoCase.Case2Extended:
return string.Format(pattern_case2, CLA, INS, P1, P2, LE);
case IsoCase.Case3Short:
case IsoCase.Case3Extended:
return string.Format(pattern_case3, CLA, INS, P1, P2, LC, BitConverter.ToString(Data).Replace("-", "").ToLower());
case IsoCase.Case4Short:
case IsoCase.Case4Extended:
return string.Format(pattern_case4, CLA, INS, P1, P2, LC, BitConverter.ToString(Data).Replace("-", "").ToLower(), LE);
default:
throw new Exception("Unknown IsoCase");
}
}
#endregion
#region Operator Overloading
public static bool operator ==(APDUCommand obj1, APDUCommand obj2)
{
return obj1.Equals(obj2);
}
public static bool operator !=(APDUCommand obj1, APDUCommand obj2)
{
return !(obj1.Equals(obj2));
2021-03-30 22:51:02 +02:00
}
2021-03-31 14:11:57 +02:00
#endregion
2021-03-30 22:51:02 +02:00
}
}