From 0a2b8c7c0ac84534c1812533a0ef81e887853f0a Mon Sep 17 00:00:00 2001 From: devnied Date: Sun, 14 Sep 2014 20:54:43 +0200 Subject: [PATCH] Fix parser issue + add unit tests Modify method getValue Read TRACK_2_EQV_DATA or TRACK2_DATA in EMV card --- .../devnied/emvnfccard/parser/EmvParser.java | 12 ++-- .../devnied/emvnfccard/utils/TlvUtil.java | 5 +- .../devnied/emvnfccard/EmvParserTest.java | 40 +++++++++++++- .../provider/PpseProviderMasterCard2Test.java | 3 - .../provider/PpseProviderMasterCard3Test.java | 55 +++++++++++++++++++ 5 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard3Test.java diff --git a/library/src/main/java/com/github/devnied/emvnfccard/parser/EmvParser.java b/library/src/main/java/com/github/devnied/emvnfccard/parser/EmvParser.java index a2dd7401..7c36b251 100644 --- a/library/src/main/java/com/github/devnied/emvnfccard/parser/EmvParser.java +++ b/library/src/main/java/com/github/devnied/emvnfccard/parser/EmvParser.java @@ -315,12 +315,7 @@ protected EmvCardScheme findCardScheme(final String pAid, final String pCardNumb * @return byte array */ protected byte[] getLogEntry(final byte[] pSelectResponse) { - byte[] ret = TlvUtil.getValue(pSelectResponse, EmvTags.LOG_ENTRY); - // Find Visa specific log entry - if (ret == null) { - ret = TlvUtil.getValue(pSelectResponse, EmvTags.VISA_LOG_ENTRY); - } - return ret; + return TlvUtil.getValue(pSelectResponse, EmvTags.LOG_ENTRY, EmvTags.VISA_LOG_ENTRY); } /** @@ -385,7 +380,7 @@ protected EmvCard extractCommonsCardData(final byte[] pGpo) throws Communication for (int index = afl.getFirstRecord(); index <= afl.getLastRecord(); index++) { byte[] info = provider.transceive(new CommandApdu(CommandEnum.READ_RECORD, index, afl.getSfi() << 3 | 4, 0) .toBytes()); - if (ResponseUtils.isEquals(data, SwEnum.SW_6C)) { + if (ResponseUtils.isEquals(info, SwEnum.SW_6C)) { info = provider.transceive(new CommandApdu(CommandEnum.READ_RECORD, index, afl.getSfi() << 3 | 4, info[info.length - 1]).toBytes()); } @@ -493,7 +488,8 @@ protected List extractAfl(final byte[] pAfl) { */ protected EmvCard extractTrack2Data(final byte[] pData) { EmvCard card = null; - byte[] track2 = TlvUtil.getValue(pData, EmvTags.TRACK_2_EQV_DATA); + byte[] track2 = TlvUtil.getValue(pData, EmvTags.TRACK_2_EQV_DATA, EmvTags.TRACK2_DATA); + if (track2 != null) { card = new EmvCard(); BitUtils bit = new BitUtils(track2); diff --git a/library/src/main/java/com/github/devnied/emvnfccard/utils/TlvUtil.java b/library/src/main/java/com/github/devnied/emvnfccard/utils/TlvUtil.java index c6eeb084..e07a5074 100644 --- a/library/src/main/java/com/github/devnied/emvnfccard/utils/TlvUtil.java +++ b/library/src/main/java/com/github/devnied/emvnfccard/utils/TlvUtil.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import com.github.devnied.emvnfccard.enums.SwEnum; @@ -361,7 +362,7 @@ public static List getlistTLV(final byte[] pData, final ITag pTag, final bo * tag to find * @return tag value or null */ - public static byte[] getValue(final byte[] pData, final ITag pTag) { + public static byte[] getValue(final byte[] pData, final ITag... pTag) { byte[] ret = null; @@ -371,7 +372,7 @@ public static byte[] getValue(final byte[] pData, final ITag pTag) { while (stream.available() > 0) { TLV tlv = TlvUtil.getNextTLV(stream); - if (tlv.getTag() == pTag) { + if (ArrayUtils.contains(pTag, tlv.getTag())) { return tlv.getValueBytes(); } else if (tlv.getTag().isConstructed()) { ret = TlvUtil.getValue(tlv.getValueBytes(), pTag); diff --git a/library/src/test/java/com/github/devnied/emvnfccard/EmvParserTest.java b/library/src/test/java/com/github/devnied/emvnfccard/EmvParserTest.java index c282656e..c9d9df51 100644 --- a/library/src/test/java/com/github/devnied/emvnfccard/EmvParserTest.java +++ b/library/src/test/java/com/github/devnied/emvnfccard/EmvParserTest.java @@ -26,6 +26,7 @@ import com.github.devnied.emvnfccard.parser.IProvider; import com.github.devnied.emvnfccard.provider.ExceptionProviderTest; import com.github.devnied.emvnfccard.provider.PpseProviderMasterCard2Test; +import com.github.devnied.emvnfccard.provider.PpseProviderMasterCard3Test; import com.github.devnied.emvnfccard.provider.PpseProviderMasterCardTest; import com.github.devnied.emvnfccard.provider.PpseProviderVisa2Test; import com.github.devnied.emvnfccard.provider.PpseProviderVisa3Test; @@ -35,6 +36,7 @@ import com.github.devnied.emvnfccard.provider.ProviderSelectPaymentEnvTest; import com.github.devnied.emvnfccard.provider.ProviderVisaCardAidTest; import com.github.devnied.emvnfccard.provider.PseProviderTest; +import com.github.devnied.emvnfccard.utils.TlvUtil; import fr.devnied.bitlib.BytesUtils; @@ -151,7 +153,39 @@ public void testPPSEMasterCard2() throws CommunicationException { Assertions.assertThat(card.getLeftPinTry()).isEqualTo(2); Assertions.assertThat(card.getApplicationLabel()).isEqualTo(null); SimpleDateFormat sdf = new SimpleDateFormat("MM/yyyy"); - Assertions.assertThat(sdf.format(card.getExpireDate())).isEqualTo("10/2002"); + Assertions.assertThat(sdf.format(card.getExpireDate())).isEqualTo("07/2002"); + Assertions.assertThat(card.getListTransactions()).isNotEmpty(); + Assertions.assertThat(card.getListTransactions().size()).isEqualTo(10); + EmvTransactionRecord record = card.getListTransactions().get(0); + Assertions.assertThat(record.getAmount()).isEqualTo(2200); + Assertions.assertThat(record.getCyptogramData()).isEqualTo("40"); + Assertions.assertThat(record.getCurrency()).isEqualTo(CurrencyEnum.TRY); + Assertions.assertThat(record.getTransactionDate()).isNotNull(); + SimpleDateFormat sdf2 = new SimpleDateFormat("dd/MM/yyyy"); + Assertions.assertThat(sdf2.format(record.getTransactionDate())).isEqualTo("12/01/2011"); + + } + + @Test + public void testPPSEMasterCard3() throws CommunicationException { + + IProvider prov = new PpseProviderMasterCard3Test(); + + EmvParser parser = new EmvParser(prov, true); + EmvCard card = parser.readEmvCard(); + + if (card != null) { + LOGGER.debug(card.toString()); + } + Assertions.assertThat(card).isNotNull(); + Assertions.assertThat(card.getAid()).isEqualTo("A0000000041010"); + Assertions.assertThat(card.getCardNumber()).isEqualTo("5200000000000000"); + Assertions.assertThat(card.getType()).isEqualTo(EmvCardScheme.MASTER_CARD); + Assertions.assertThat(card.getHolderName()).isEqualTo(null); + Assertions.assertThat(card.getLeftPinTry()).isEqualTo(2); + Assertions.assertThat(card.getApplicationLabel()).isEqualTo(null); + SimpleDateFormat sdf = new SimpleDateFormat("MM/yyyy"); + Assertions.assertThat(sdf.format(card.getExpireDate())).isEqualTo("11/2019"); Assertions.assertThat(card.getListTransactions()).isNotEmpty(); Assertions.assertThat(card.getListTransactions().size()).isEqualTo(10); EmvTransactionRecord record = card.getListTransactions().get(0); @@ -351,10 +385,10 @@ public void testgetLogFormat() throws Exception { @Test public void testGetLogEntry() throws Exception { byte[] selectResponse = BytesUtils - .fromString("6F 37 84 07 A0 00 00 00 42 10 10 A5 2C 9F 38 18 9F 66 04 9F 02 06 9F 03 06 9F 1A 02 95 05 5F 2A 02 9A 03 9C 01 9F 37 04 BF 0C 0E DF 60 02 0B 1E DF 61 01 03 9F 4D 02 0B 11 90 00"); + .fromString("6F 37 84 07 A0 00 00 00 42 10 10 A5 2C 9F 38 18 9F 66 04 9F 02 06 9F 03 06 9F 1A 02 95 05 5F 2A 02 9A 03 9C 01 9F 37 04 BF 0C 0E DF 62 02 0B 1E DF 61 01 03 9F 4D 02 0B 11 90 00"); + System.out.println(TlvUtil.prettyPrintAPDUResponse(selectResponse)); byte[] data = Whitebox.invokeMethod(new EmvParser(null, true), EmvParser.class, "getLogEntry", selectResponse); - Assertions.assertThat(BytesUtils.bytesToString(data)).isEqualTo("0B 11"); selectResponse = BytesUtils .fromString("6F 32 84 07 A0 00 00 00 42 10 10 A5 27 9F 38 18 9F 66 04 9F 02 06 9F 03 06 9F 1A 02 95 05 5F 2A 02 9A 03 9C 01 9F 37 04 BF 0C 09 DF 60 02 0B 1E DF 61 01 03 90 00"); data = Whitebox.invokeMethod(new EmvParser(null, true), EmvParser.class, "getLogEntry", selectResponse); diff --git a/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard2Test.java b/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard2Test.java index d7bae2ed..69f47267 100644 --- a/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard2Test.java +++ b/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard2Test.java @@ -33,9 +33,6 @@ public byte[] transceive(final byte[] pCommand) { response = "70 6B 9F 6C 02 00 01 9F 62 06 00 00 00 00 0E 00 9F 63 06 00 00 00 00 00 7E 56 2A 42 35 30 30 32 30 30 30 30 30 35 34 35 37 37 32 36 5E 20 2F 5E 32 31 31 32 32 30 32 30 30 30 30 30 31 30 30 30 30 30 30 30 30 9F 64 01 03 9F 65 02 00 0E 9F 66 02 0E 70 9F 6B 13 52 00 00 00 00 00 00 00 D0 11 92 02 00 00 01 00 00 00 0F 9F 67 01 03 90 00"; break; case 4: - response = "70 81 B6 5F 25 03 13 12 01 5F 24 03 21 12 31 5A 08 52 00 00 00 00 00 00 00 5F 34 01 01 9F 07 02 3D 00 9F 08 02 00 02 8C 27 9F 02 06 9F 03 06 9F 1A 02 95 05 5F 2A 02 9A 03 9C 01 9F 37 04 9F 35 01 9F 45 02 9F 4C 08 9F 34 03 9F 21 03 9F 7C 14 8D 0C 91 0A 8A 02 95 05 9F 37 04 9F 4C 08 8E 0E 00 00 00 00 00 00 00 00 42 03 1E 03 1F 03 9F 51 03 9F 37 04 9F 5B 0C DF 60 08 DF 61 08 DF 62 01 DF 63 A0 9F 0D 05 B4 50 84 00 00 9F 0E 05 00 10 80 00 00 9F 0F 05 B4 70 84 80 00 5F 28 02 07 92 9F 4A 01 82 57 13 52 00 00 00 00 00 00 00 D0 12 22 26 70 10 10 00 00 00 0F 90 00"; - break; - case 5: response = "9F 4F 1A 9F 27 01 9F 02 06 5F 2A 02 9A 03 9F 36 02 9F 52 06 DF 3E 01 9F 21 03 9F 7C 14 90 00"; break; diff --git a/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard3Test.java b/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard3Test.java new file mode 100644 index 00000000..cd314dbd --- /dev/null +++ b/library/src/test/java/com/github/devnied/emvnfccard/provider/PpseProviderMasterCard3Test.java @@ -0,0 +1,55 @@ +package com.github.devnied.emvnfccard.provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.devnied.emvnfccard.parser.IProvider; +import com.github.devnied.emvnfccard.utils.TlvUtil; + +import fr.devnied.bitlib.BytesUtils; + +public class PpseProviderMasterCard3Test implements IProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(PpseProviderMasterCard3Test.class); + + private int step; + + @Override + public byte[] transceive(final byte[] pCommand) { + String response = null; + LOGGER.debug("send: " + BytesUtils.bytesToString(pCommand)); + switch (step++) { + + case 0: + response = "6F 23 84 0E 32 50 41 59 2E 53 59 53 2E 44 44 46 30 31 A5 11 BF 0C 0E 61 0C 4F 07 A0 00 00 00 04 10 10 87 01 01 90 00"; + break; + case 1: + response = "6F 32 84 07 A0 00 00 00 04 10 10 A5 27 50 0A 4D 61 73 74 65 72 43 61 72 64 87 01 01 5F 2D 02 66 72 BF 0C 10 9F 4D 02 0B 0A 5F 56 03 43 41 4E DF 62 02 40 80 90 00"; + break; + case 2: + response = "77 0A 82 02 00 00 94 04 08 01 01 00 90 00"; + break; + case 3: + response = "70 81 8C 9F 6C 02 00 01 9F 62 06 00 00 00 00 07 00 9F 63 06 00 00 00 00 00 FE 56 41 42 33 31 39 00 33 33 00 30 30 35 33 31 39 00 37 34 00 20 2F 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 5E 31 38 30 31 31 30 31 30 31 30 30 30 30 30 30 30 30 30 30 33 9F 64 01 04 9F 65 02 07 00 9F 66 02 00 FE 9F 6B 13 52 00 00 00 00 00 00 00 D1 91 11 01 01 00 00 00 00 00 3F 9F 67 01 04 9F 6E 07 02 21 00 00 30 30 00 90 00"; + break; + case 4: + response = "9F 4F 1A 9F 27 01 9F 02 06 5F 2A 02 9A 03 9F 36 02 9F 52 06 DF 3E 01 9F 21 03 9F 7C 14 90 00"; + break; + + default: + response = "40 00 00 00 00 22 00 09 49 11 01 12 00 E0 61 90 22 21 00 00 00 00 50 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00"; + } + + if (BytesUtils.bytesToStringNoSpace(pCommand).equals("80CA9F1700")) { + response = "9F 17 01 02 90 00"; + } + + LOGGER.debug("resp: " + response); + byte[] ret = BytesUtils.fromString(response); + try { + LOGGER.debug(TlvUtil.prettyPrintAPDUResponse(ret)); + } catch (Exception e) { + } + return ret; + } +}