Skip to content

Commit

Permalink
updated
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfehr committed Mar 28, 2023
1 parent b9b35dd commit 847a7ed
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 133 deletions.
2 changes: 1 addition & 1 deletion .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,12 @@ public class DolValues {

private List<DolTag> dolList = new ArrayList<>();
// used in PDOL
//private final DolTag t9f66 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66}, "Terminal Transaction Qualifiers", hexBlankToBytes("A0 00 00 00")); // runs on all my cards but not returns not all afl
// test for visa comd, seems to be the better option ()
private final DolTag t9f66 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66}, "Terminal Transaction Qualifiers", hexBlankToBytes("27 00 00 00")); // default
private final DolTag t9f6600 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66, (byte) 0x00}, "Terminal Transaction Qualifiers", hexBlankToBytes("27 00 00 00")); // default
private final DolTag t9f6601 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66, (byte) 0x01}, "Terminal Transaction Qualifiers", hexBlankToBytes("B7 60 40 00")); // does not run with Lloyds Visa
private final DolTag t9f6602 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66, (byte) 0x02}, "Terminal Transaction Qualifiers", hexBlankToBytes("A0 00 00 00")); // runs on all my cards but returns not all afl
private final DolTag t9f6603 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66, (byte) 0x03}, "Terminal Transaction Qualifiers", hexBlankToBytes("F0 20 40 00")); // this fails on DKB debit card
// online decoder: https://paymentcardtools.com/emv-tag-decoders/ttq
//private final DolTag t9f66 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66}, "Terminal Transaction Qualifiers", hexBlankToBytes("B7 60 40 00")); // does not run with Lloyds Visa
private final DolTag t9f66 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66}, "Terminal Transaction Qualifiers", hexBlankToBytes("27 00 00 00"));

/*
https://stackoverflow.com/a/68996365/8166854
Your Terminal Transaction Qualifier byte 1 bit 1 is set to zero, meaning "Offline Data Authentication for Online Authorizations
not supported". Try setting it to 1: B6 60 40 00 --> B7 60 40 00.
I was having the same issue and this was enough to receive an AFL.
*/

//private final DolTag t9f66 = setTag(new byte[]{(byte) 0x9f, (byte) 0x66}, "Terminal Transaction Qualifiers", hexBlankToBytes("F0 20 40 00")); // this fails on DKB debit card
private final DolTag t9f02 = setTag(new byte[]{(byte) 0x9f, (byte) 0x02}, "Transaction Amount", hexBlankToBytes("00 00 00 00 10 00")); // 00 00 00 00 10 00
private final DolTag t9f03 = setTag(new byte[]{(byte) 0x9f, (byte) 0x03}, "Amount, Other (Numeric)", hexBlankToBytes("00 00 00 00 00 00"));
private final DolTag t9f1a = setTag(new byte[]{(byte) 0x9f, (byte) 0x1a}, "Terminal Country Code", hexBlankToBytes("09 78")); // eur
Expand All @@ -40,54 +32,6 @@ public class DolValues {
private final DolTag t9f7c = setTag(new byte[]{(byte) 0x9f, (byte) 0x7c}, "Merchant Custom Data", hexBlankToBytes("00 00 00 00 00 00 00 00 00 00 00 00 00 00"));
private final DolTag t00 = setTag(new byte[]{(byte) 0x00}, "Tag not found", hexBlankToBytes("00"));

/*
I/System.out: 8C 27 -- Card Risk Management Data Object List 1 (CDOL1)
I/System.out: 9F 02 06 -- Amount, Authorised (Numeric)
I/System.out: 9F 03 06 -- Amount, Other (Numeric)
I/System.out: 9F 1A 02 -- Terminal Country Code
I/System.out: 95 05 -- Terminal Verification Results (TVR)
I/System.out: 5F 2A 02 -- Transaction Currency Code
I/System.out: 9A 03 -- Transaction Date
I/System.out: 9C 01 -- Transaction Type
I/System.out: 9F 37 04 -- Unpredictable Number
I/System.out: 9F 35 01 -- Terminal Type
I/System.out: 9F 45 02 -- Data Authentication Code
I/System.out: 9F 4C 08 -- ICC Dynamic Number
I/System.out: 9F 34 03 -- Cardholder Verification (CVM) Results
I/System.out: 9F 21 03 -- Transaction Time (HHMMSS)
I/System.out: 9F 7C 14 -- Merchant Custom Data
CDOL1 MC
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, // amount ok
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // other amount ok
0x06, 0x42, // terminal country ok
0x00, 0x00, 0x00, 0x00, 0x00, // tvr terminal ok
0x09, 0x46, // currency code ok
0x20, 0x08, 0x23, // transaction date ok, todo fix date ?
0x00, // transaction type ok
0x11, 0x22, 0x33, 0x44, // UN ok
0x22, // terminal type ok
0x00, 0x00,// data auth code ok
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // icc dynamic ok
0x00, 0x00, 0x00, // cvm results ok
0x11, 0x10, 0x09, // Transaction Time (HHMMSS) added
//0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 cut
//0x54, 0x11, // 2 merchant category cut
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
*/



/*
9f66 04 Terminal Transaction Qualifiers : B6 60 40 00
9f02 06 Transaction Amount : 00 00 00 01 00 00
9f03 06 Amount, Other (Numeric) always: 00 00 00 00 00 00
9f1a 02 Terminal Country Code : 08 26 UK
95 05 Terminal Verificat.Results alway: 00 00 00 00 00
5f2a 02 Transaction Currency Code : 08 26
9a 03 Transaction Date : 23 03 03
9c 01 Transaction Type : 00
9f37 04 Unpredictable Number : 38 39 30 31
*/
public DolValues() {
// empty constructor to fill the emvTagList
}
Expand All @@ -112,6 +56,32 @@ public byte[] getDolValue(byte[] tagByte) {
return null; // default, entry not found
}

/**
* same method as getDolValue but returns alternate tag 9966 Terminal Transaction Qualifiers values
* defined at the moment [00] .. [03]
* @param tagByte
* @param alternateTag9966Ttq
* @return
*/
public byte[] getDolValue(byte[] tagByte, byte[] alternateTag9966Ttq) {
// concatenates getDolValue | alternateTag9966Ttq if tagByte = 0x9f66
byte[] tagByteLookup;
if (Arrays.equals(tagByte, new byte[]{(byte) 0x9f, (byte) 0x66})) {
tagByteLookup = new byte[3];
System.arraycopy(tagByte,0, tagByteLookup, 0, 2);
System.arraycopy(alternateTag9966Ttq, 0, tagByteLookup, 2, 1);
} else {
tagByteLookup = tagByte.clone();
}
for (int i = 0; i < dolList.size(); i++) {
DolTag dolTag = dolList.get(i);
if (Arrays.equals(dolTag.getTag(), tagByteLookup)) {
return dolTag.getDefaultValue();
}
}
return null; // default, entry not found
}

public String dump() {
StringBuilder sb = new StringBuilder();
sb.append("List of predefined tag and values for PDOL and CDOL").append("\n");
Expand Down Expand Up @@ -167,7 +137,7 @@ private String trimStringRight(String data, int len) {
* @param bytes
* @return hex encoded string
*/
public static String bytesToHexNpe(byte[] bytes) {
private static String bytesToHexNpe(byte[] bytes) {
if (bytes != null) {
StringBuffer result = new StringBuffer();
for (byte b : bytes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,47 +148,6 @@ public void onTagDiscovered(Tag tag) {
* step 1 code end
*/

/*
// get the tags from respond
BerTlvParser parserA = new BerTlvParser();
BerTlvs tlvs = parserA.parse(selectPpseResponseOk, 0, selectPpseResponseOk.length);
List<BerTlv> selectPpseResponseTagList = tlvs.getList();
int selectPpseResponseTagListSize = selectPpseResponseTagList.size();
writeToUiAppend("found " + selectPpseResponseTagListSize + " tags in response");
// show iterating
for (int i = 0; i < selectPpseResponseTagListSize; i++) {
BerTlv tlv = selectPpseResponseTagList.get(i);
writeToUiAppend(tlv.toString());
BerTag berTag = tlv.getTag();
boolean berTagIsConstructed = berTag.isConstructed();
}
*/

/*
// devnied
writeToUiAppend("");
List<TagAndLength> parsedList = TlvUtil.parseTagAndLength(selectPpseResponseOk);
int parsedListSize = parsedList.size();
writeToUiAppend("parsedListSize: " + parsedListSize);
writeToUiAppend(parsedList.toString());
*/
/*
// this is working
writeToUiAppend("");
writeToUiAppend("using TagListParser");
List<TagNameValue> parsedList = TagListParser.parseRespond(selectPpseResponseOk);
int parsedListSize = parsedList.size();
writeToUiAppend("parsedListSize: " + parsedListSize);
for (int i = 0; i < parsedListSize; i++) {
TagNameValue p = parsedList.get(i);
writeToUiAppend("tag " + i + " : " + bytesToHexNpe(p.getTagBytes()) + " has the value " + bytesToHexNpe(p.getTagValueBytes()));
writeToUiAppend("tag " + i + " : " + bytesToHexNpe(p.getTagBytes()) + " has the name " + p.getTagName());
}
*/

/**
* step 2 code start
*/
Expand Down Expand Up @@ -315,7 +274,8 @@ public void onTagDiscovered(Tag tag) {
//gpoRequestCommand = getGetProcessingOptionsFromPdol(pdolValue); // not working for DKB Visa
*/
writeToUiAppend("found tag 0x9F38 (PDOL) in the selectAid with this length: " + pdolValue.length + " data: " + bytesToHexNpe(pdolValue));
byte[][] gpoRequestCommandArray = getGpoFromPdolExtended(pdolValue, 0);
byte[][] gpoRequestCommandArray = getGpoFromPdolExtended(pdolValue, new byte[]{(byte) 0x00}); // 00 = default, maximum 03

gpoRequestCommand = gpoRequestCommandArray[0];
String pdolRequestString = new String(gpoRequestCommandArray[1], StandardCharsets.UTF_8);
writeToUiAppend("");
Expand All @@ -330,7 +290,7 @@ public void onTagDiscovered(Tag tag) {

writeToUiAppend("No PDOL found in the selectAid response, generating a 'null' PDOL");
//gpoRequestCommand = getGpoFromPdol(new byte[0]); // empty PDOL
byte[][] gpoRequestCommandArray = getGpoFromPdolExtended(new byte[0], 0);
byte[][] gpoRequestCommandArray = getGpoFromPdolExtended(new byte[0], new byte[]{(byte) 0x00});
gpoRequestCommand = gpoRequestCommandArray[0];
String pdolRequestString = new String(gpoRequestCommandArray[1], StandardCharsets.UTF_8);
writeToUiAppend("");
Expand Down Expand Up @@ -365,9 +325,6 @@ public void onTagDiscovered(Tag tag) {
}
// todo check for null value when gpo response is null due to wrong command

// todo: if a (Visa-) card doesn't like the gpoRequest we should try with another 'Terminal Transaction Qualifiers' value,
// todo: e.q. 'A0 00 00 00' or 'B7 60 40 00' instead of '27 00 00 00'

/**
* step 5 code end
*/
Expand Down Expand Up @@ -693,12 +650,12 @@ private byte[] getGetProcessingOptionsFromPdol(final byte[] pPdol) throws Commun
* construct the getProcessingOptions command using the provided pdol
* the default ttq is null, but another ttq can used if default ttq gives no result for later sending
* @param pdol
* @param ttq
* @param alternativeTtq
* @return a byte[][] array
* [0] = getProcessingOptions command
* [1] = text table with requested tags from pdol with length and value
*/
private byte[][] getGpoFromPdolExtended(@NonNull byte[] pdol, int ttq) {
private byte[][] getGpoFromPdolExtended(@NonNull byte[] pdol, byte[] alternativeTtq) {
// todo implement alternative ttq

byte[][] result = new byte[2][];
Expand Down Expand Up @@ -736,27 +693,27 @@ private byte[][] getGpoFromPdolExtended(@NonNull byte[] pdol, int ttq) {
String nameOfTag = tal.getTag().getName();
valueOfTagSum += tal.getLength(); // add it to the sum
// now we are trying to find a default value
byte[] defaultValue = dolValues.getDolValue(tagToSearch);
byte[] defaultValue = dolValues.getDolValue(tagToSearch, alternativeTtq);
byte[] usedValue = new byte[0];
if (defaultValue != null) {
if (defaultValue.length > lengthOfTag) {
// cut it to correct length
usedValue = Arrays.copyOfRange(defaultValue, 0, lengthOfTag);
Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " default is too long, cut to: " + bytesToHexNpe(usedValue));
//Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " default is too long, cut to: " + bytesToHexNpe(usedValue));
} else if (defaultValue.length < lengthOfTag) {
// increase length
usedValue = new byte[lengthOfTag];
System.arraycopy(defaultValue, 0, usedValue, 0, defaultValue.length);
Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " default is too short, increased to: " + bytesToHexNpe(usedValue));
//Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " default is too short, increased to: " + bytesToHexNpe(usedValue));
} else {
// correct length
usedValue = defaultValue.clone();
Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " default found: " + bytesToHexNpe(usedValue));
//Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " default found: " + bytesToHexNpe(usedValue));
}
} else {
// defaultValue is null means the tag was not found in our tags database for default values
usedValue = new byte[lengthOfTag];
Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " NO default found, generate zeroed: " + bytesToHexNpe(usedValue));
//Log.i(TAG, "asked for tag: " + bytesToHexNpe(tal.getTag().getTagBytes()) + " NO default found, generate zeroed: " + bytesToHexNpe(usedValue));
}
// now usedValue does have the correct length
sb.append(bytesToHexNpe(usedValue));
Expand All @@ -770,7 +727,6 @@ private byte[][] getGpoFromPdolExtended(@NonNull byte[] pdol, int ttq) {
result[0] = hexToBytes(constructedGpoCommandString);
result[1] = returnString.toString().getBytes(StandardCharsets.UTF_8);
return result;
//return hexToBytes(constructedGpoCommandString);
}

/**
Expand Down Expand Up @@ -849,22 +805,10 @@ private byte[] getChallenge(IsoDep nfc) {
try {
result = nfc.transceive(cmd);
} catch (IOException e) {
Log.e(TAG, "* getApplicationTransactionCounter failed");
Log.e(TAG, "* getChallenge failed");
return null;
}
return result;
/*
//System.out.println("*** getATC: " + bytesToHexNpe(result));
// e.g. visa returns 9f360200459000
// e.g. visa returns 9f36020045 9000
byte[] resultOk = checkResponse(result);
if (resultOk == null) {
return null;
} else {
return getTagValueFromResult(resultOk, (byte) 0x9f, (byte) 0x36);
}
*/
}

// Get the data of ATC(Application Transaction Counter, tag '9F36')), template 77 or 80
Expand Down Expand Up @@ -940,7 +884,7 @@ private byte[] getLogFormat(IsoDep nfc) {
}

/**
* gets the byte value of a tag from transceive response
* gets the byte value of a tag from a transceive response
*
* @param data
* @param search
Expand Down Expand Up @@ -1447,8 +1391,8 @@ private void writeStringToExternalSharedStorage() {
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
//boolean pickerInitialUri = false;
//intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
// boolean pickerInitialUri = false;
// intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
// get filename from edittext
String filename = exportStringFileName;
// sanity check
Expand Down

0 comments on commit 847a7ed

Please sign in to comment.