Reputation: 3739
I have tried many sample codes to parse APDU response to TLV format. I am able to parse it properly if the response length is less but facing issue if length is more(how calculate length of a tag without any libraries)
NOTE: I am using predefined tags in Constants
code:
private HashMap<String, String> parseTLV(String apduResponse) {
HashMap<String, String> tagValue = new HashMap<>();
String remainingApdu = apduResponse.replaceAll(" ", "");
if (remainingApdu.endsWith(ResponseTags._SUCCESS_STATUSWORDS)) {
remainingApdu = remainingApdu.substring(0, remainingApdu.length() - 4);
}
while (remainingApdu != null && remainingApdu.length() > 2) {
remainingApdu = addTagValue(tagValue, remainingApdu);
}
return tagValue;
}
addTagValue method
private String addTagValue(HashMap<String, String> tagValue, String apduResponse) {
String tag = "";
String length = "";
String value = "";
int tagLen = 0;
if (tagUtils.isValidTag(apduResponse.substring(0, 2))) {
tagLen = readTagLength(apduResponse.substring(3));
// tagLen = 2;
tag = apduResponse.substring(0, 2);
} else if (tagUtils.isValidTag(apduResponse.substring(0, 4))) {
tagLen = 4;
tag = apduResponse.substring(0, 4);
} else {
return "";
}
Log.e("TAG_LEN","tag: "+tag+"taglen: "+tagLen);
if (tagUtils.shouldCheckValueFor(tag)) {
length = apduResponse.substring(tagLen, tagLen + 2);
int len = tagUtils.hexToDecimal(length);
value = apduResponse.substring(tagLen + 2, (len * 2) + tagLen + 2);
tagValue.put(tag, value);
if (ResponseTags.getRespTagsmap().containsKey(tag)) {
//logData = logData + "\nKEY:" + tag + " TAG:" + ResponseTags.getRespTagsmap().get(tag)/* + " VALUE:" + value + "\n "*/;
}
if (tagUtils.isTemplateTag(tag)) {
// logData = logData + "\n\t-->";
return addTagValue(tagValue, value) + apduResponse.substring(tag.length() + value.length() + length.length());
} else {
return apduResponse.substring(tag.length() + value.length() + length.length());
}
} else {
value = apduResponse.substring(2, 4);
tagValue.put(tag, value);
// logData = logData + "\n\t\tKEY:" + tag + " TAG:" + ResponseTags.getRespTagsmap().get(tag) /*+ " VALUE:" + value + "\n "*/;
return apduResponse.substring(tag.length() + value.length() + length.length());
}
}
readTagLength :
private int readTagLength(String apduResponse) {
int len_bytes = 0;
if (apduResponse.length() > 2) {
len_bytes = (apduResponse.length()) / 2;
}
Log.e("tlv length:", "bytes:" + len_bytes);
if (len_bytes < 128) {
return 2;
} else if (len_bytes > 127 && len_bytes < 255) {
return 4;
} else {
return 6;
}
}
I cannot able to get length properly for few cards(if apdu response is long) Please help
Upvotes: 3
Views: 6397
Reputation: 337
Below sample has an assumption that TLV array is ending with special 0x00 tag but for sure you can ignore it.
Pojo class :
public class Tlv {
private short tag;
private byte[] value;
public Tlv(short tag) {
this.tag = tag;
}
public short getTag() {
return tag;
}
public byte[] getValue() {
return value;
}
public void setValue(byte[] valueBytes) {
this.value = valueBytes;
}
}
Utility method :
public static Map<Byte, Tlv> parse(ByteBuffer bb) throws TlvException {
Map<Byte, Tlv> tlvs = null;
tlvs = new HashMap<Byte, Tlv>();
try {
while (bb.remaining() > 0) {
byte tag = bb.get();
if(tag == 0x00)
continue;
int length = bb.get();
byte[] value = new byte[length];
bb.get(value, 0, length);
Tlv tlv = new Tlv(tag);
tlv.setValue(value);
tlvs.put(tag, tlv);
}
} catch (IndexOutOfBoundsException e) {
throw new TlvException("Malformed TLV part: " + bb.toString() + ".", e);
}
return tlvs;
}
Upvotes: 1
Reputation: 2211
First be sure the input data is proper before you go into the code. Take the full data and try it on https://www.emvlab.org/tlvutils/ .
Once its confirmed the data is proper, go through in EMV 4.3 Book 3, Annex B Rules for BER-TLV Data Objects sections B1, B2, B3 - with utmost attention.
If you follow this precisely, then you wouldn't need to store a static list of tags; will save time in future.
Upvotes: 1