/// <summary> /// Track Data is provided in TLV with DFDB05 and DFDB06 TAGS /// </summary> /// <param name="trackInformation"></param> /// <returns></returns> public MSRTrackData RetrieveSREDTrackData(byte[] trackInformation) { byte[] sREDMagStripTrack1 = new byte[] { 0xDF, 0xDB, 0x05 }; byte[] sREDMagStripTrack2 = new byte[] { 0xDF, 0xDB, 0x06 }; MSRTrackData trackData = new MSRTrackData() { PANData = string.Empty, Name = string.Empty, ExpirationDate = string.Empty, DiscretionaryData = string.Empty }; TLV.TLV tlv = new TLV.TLV(); List <TLV.TLV> tags = tlv.Decode(trackInformation, 0, trackInformation.Length, null); string decryptedTrack1Data = ""; string decryptedTrack2Data = ""; foreach (var tag in tags) { if (tag.Tag.SequenceEqual(sREDMagStripTrack1)) { decryptedTrack1Data = ConversionHelper.ByteArrayCodedHextoString(tag.Data); } else if (tag.Tag.SequenceEqual(sREDMagStripTrack2)) { decryptedTrack2Data = ConversionHelper.ByteArrayCodedHextoString(tag.Data); } } // clean up track data Debug.WriteLine($"DECRYPTED _: {decryptedTrack1Data}"); // expected format: PAN^NAME^ADDITIONAL-DATA^DISCRETIONARY-DATA MatchCollection match = Regex.Matches(decryptedTrack1Data, @"%B([0-9 ]{1,19})\^([^\^]{2,26})\^([0-9]{4}|\^)([0-9]{3}|\^)([^\?]+)\?", RegexOptions.Compiled); // DISCRETIONARY DATA is optional if (match.Count == 1 && match[0].Groups.Count >= 5) { // PAN DATA trackData.PANData = match[0].Groups[1].Value; // NAME trackData.Name = match[0].Groups[2].Value; // ADDITIONAL DATA trackData.ExpirationDate = match[0].Groups[3].Value; trackData.ServiceCode = match[0].Groups[4].Value; // DISCRETIONARY DATA if (match[0].Groups.Count > 5) { trackData.DiscretionaryData = match[0].Groups[5].Value; } } return(trackData); }
private void ReadResponses(byte[] responseBytes) { var validNADValues = new List <byte> { 0x01, 0x02, 0x11 }; var validPCBValues = new List <byte> { 0x00, 0x01, 0x02, 0x03, 0x40, 0x41, 0x42, 0x43 }; var nestedTagTags = new List <byte[]> { new byte[] { 0xEE }, new byte[] { 0xEF }, new byte[] { 0xF0 }, new byte[] { 0xE0 }, new byte[] { 0xE4 }, new byte[] { 0xE7 }, new byte[] { 0xFF, 0x7C }, new byte[] { 0xFF, 0x7F } }; var powerManagement = new List <byte[]> { new byte[] { 0xE6 }, new byte[] { 0xC3 }, new byte[] { 0xC4 }, new byte[] { 0x9F, 0x1C } }; var addedResponseComponent = false; lock (ReadResponsesBytesLock) { // Add current bytes to available bytes var combinedResponseBytes = new byte[ReadResponsesBytes.Length + responseBytes.Length]; // TODO ---> @JonBianco BlockCopy should be leveraging here as it is vastly superior to Array.Copy // Combine prior bytes with new bytes Array.Copy(ReadResponsesBytes, 0, combinedResponseBytes, 0, ReadResponsesBytes.Length); Array.Copy(responseBytes, 0, combinedResponseBytes, ReadResponsesBytes.Length, responseBytes.Length); // Attempt to parse first message in response buffer var consumedResponseBytes = 0; var responseCode = 0; var errorFound = false; ReadErrorLevel readErrorLevel = ReadErrorLevel.None; // Validate NAD, PCB, and LEN values if (combinedResponseBytes.Length < 4) { errorFound = true; readErrorLevel = ReadErrorLevel.Length; } else if (!validNADValues.Contains(combinedResponseBytes[0])) { errorFound = true; readErrorLevel = ReadErrorLevel.Invalid_NAD; } else if (!validPCBValues.Contains(combinedResponseBytes[1])) { errorFound = true; readErrorLevel = ReadErrorLevel.Invalid_PCB; } else if (combinedResponseBytes[2] > (combinedResponseBytes.Length - (3 + 1))) { errorFound = true; readErrorLevel = ReadErrorLevel.Invalid_CombinedBytes; } else { // Validate LRC byte lrc = 0; var index = 0; for (index = 0; index < (combinedResponseBytes[2] + 3); index++) { lrc ^= combinedResponseBytes[index]; } if (combinedResponseBytes[combinedResponseBytes[2] + 3] != lrc) { errorFound = true; readErrorLevel = ReadErrorLevel.Missing_LRC; } else if ((combinedResponseBytes[1] & 0x01) == 0x01) { var componentBytes = new byte[combinedResponseBytes[2]]; Array.Copy(combinedResponseBytes, 3, componentBytes, 0, combinedResponseBytes[2]); ReadResponseComponentBytes.Add(componentBytes); consumedResponseBytes = combinedResponseBytes[2] + 3 + 1; errorFound = true; readErrorLevel = ReadErrorLevel.CombinedBytes_MisMatch; addedResponseComponent = true; } else { var sw1Offset = combinedResponseBytes[2] + 3 - 2; //if ((combinedResponseBytes[sw1Offset] != 0x90) && (combinedResponseBytes[sw1Offset + 1] != 0x00)) // errorFound = true; responseCode = (combinedResponseBytes[sw1Offset] << 8) + combinedResponseBytes[sw1Offset + 1]; } } if (!errorFound) { var totalDecodeSize = combinedResponseBytes[2] - 2; // Use LEN of final response packet foreach (var component in ReadResponseComponentBytes) { totalDecodeSize += component.Length; } var totalDecodeBytes = new byte[totalDecodeSize]; var totalDecodeOffset = 0; foreach (var component in ReadResponseComponentBytes) { Array.Copy(component, 0, totalDecodeBytes, totalDecodeOffset, component.Length); totalDecodeOffset += component.Length; } Array.Copy(combinedResponseBytes, 3, totalDecodeBytes, totalDecodeOffset, combinedResponseBytes[2] - 2); // Skip final response header and use LEN of final response (no including the SW1, SW2, and LRC) ReadResponseComponentBytes = new List <byte[]>(); if (ResponseTagsHandler != null || ResponseContactlessHandler != null) { TLV.TLV tlv = new TLV.TLV(); var tags = tlv.Decode(totalDecodeBytes, 0, totalDecodeBytes.Length, nestedTagTags); //PrintTags(tags); if (ResponseTagsHandler != null) { ResponseTagsHandler.Invoke(tags, responseCode); } else if (ResponseContactlessHandler != null) { ResponseContactlessHandler.Invoke(tags, responseCode, combinedResponseBytes[1]); } } else if (ResponseTaglessHandler != null) { ResponseTaglessHandler.Invoke(totalDecodeBytes, responseCode); } consumedResponseBytes = combinedResponseBytes[2] + 3 + 1; // Consumed NAD, PCB, LEN, [LEN] bytes, and LRC addedResponseComponent = (combinedResponseBytes.Length - consumedResponseBytes) > 0; } else { // allows for debugging of VIPA read issues System.Diagnostics.Debug.WriteLine($"VIPA-READ: ON PORT={commPort} - ERROR LEVEL: '{readErrorLevel}'"); } // Remove consumed bytes and leave remaining bytes for later consumption var remainingResponseBytes = new byte[combinedResponseBytes.Length - consumedResponseBytes]; Array.Copy(combinedResponseBytes, consumedResponseBytes, remainingResponseBytes, 0, combinedResponseBytes.Length - consumedResponseBytes); ReadResponsesBytes = remainingResponseBytes; } if (addedResponseComponent) { ReadResponses(Array.Empty <byte>()); } }