using System; using System.Collections.Generic; using System.Linq; using NFC.Mifare_DESFire; namespace NFC.ISO7816_4 { public class APDUResponse { #region constructor public APDUResponse() { } /// /// Creates a new APDUResponse from the raw received data. /// public APDUResponse(byte[] raw) { Body = raw.Take(raw.Length - 1).ToArray(); SW1 = raw[raw.Length - 2]; SW2 = raw[raw.Length - 3]; } #endregion #region Properties /// /// ISO 7816-4-4 - Body - Body /// public byte[] Body { get; set; } /// /// ISO 7816-4 - SW1 - Status Word 1 /// public byte SW1 { get; set; } /// /// ISO 7816-4 - SW2 - Status Word 2 /// public byte SW2 { get; set; } public APDUStatusWords StatusWord { get { // Some status words only require a specific first byte // and in some cases SW2 contains additional information. // This will filter out those errors. When there is more information separate methods for getting those are available. switch(SW1) { case 0x61: // Kommando erfolgreich ausgeführt. xx Datenbytes können mit dem ‚GET RESPONSE‘-Kommando abgeholt werden. Statuswort zur Steuerung des T=0-Protokolls return APDUStatusWords.DATA_READY; case 0x62: // Warnung; Zustand des nichtflüchtigen Speichers nicht verändert return APDUStatusWords.STORAGE_NOT_CHANGED; case 0x63: if((SW2 & 0xF0) == 0xC0) { // Zähler hat den Wert x erreicht (die genaue Bedeutung ist vom Kommando abhängig) return APDUStatusWords.COUNTER_REACHED; } // Warnung; Zustand des nichtflüchtigen Speichers verändert return APDUStatusWords.STORAGE_CHANGED; case 0x64: // Ausführungsfehler; Zustand des nichtflüchtigen Speichers nicht verändert return APDUStatusWords.EXECUTION_ERROR_WITHOUT_CHANGE; case 0x65: // Ausführungsfehler; Zustand des nichtflüchtigen Speichers verändert return APDUStatusWords.EXECUTION_ERROR_WITH_CHANGE; case 0x6C: // Falsche Länge Le; xx gibt die korrekte Länge an Statuswort zur Steuerung des T=0-Protokolls return APDUStatusWords.INVALID_LE; } return (APDUStatusWords) (((UInt16) SW1) << 8 | ((UInt16) SW2)); } } /// /// If the reponse status is DATA_READY this method can be used to get the amount of data that can be read from the card. /// public byte DataLength { get { return SW2; } } /// /// If the reponse status is COUNTER_REACHED this method can be used to get the value that the counter reached. /// public byte Counter { get { return (byte)(SW2 & 0x0F); } } /// /// If the reponse status is INVALID_LE this method can be used to get the correct LE. /// public byte CorrectLE { get { return SW2; } } #endregion #region Methodes public byte[] ToArray() { byte[] array = null; if (Body != null) { array = new byte[Body.Length + 2]; Body.CopyTo(array, 0); array[Body.Length] = SW1; array[Body.Length + 1] = SW2; } else { array = new byte[2]; array[0] = SW1; array[1] = SW2; } return array; } public override bool Equals(object obj) { return obj is APDUResponse response && EqualityComparer.Default.Equals(Body, response.Body) && SW1 == response.SW1 && SW2 == response.SW2; } public override int GetHashCode() { return HashCode.Combine(Body, SW1, SW2); } public override string ToString() { if(Body == null) { return string.Format("SW1: 0x{0:x} | SW2: 0x{1:x}", SW1, SW2); } else { return string.Format("SW1: 0x{0:x} | SW2: 0x{1:x} | Body: {2:x}", SW1, SW2, BitConverter.ToString(Body).Replace("-", "").ToLower()); } } #endregion } }