For example I have R-APDU like this
uchar rApdu[] = {0x6F, 0x35 ,0x84 ,0x08 ,0x45 ,0x4F ,0x50 ,
0x43 ,0x43 ,0x41 ,0x52 ,0x44 ,0xA5 ,0x29 ,0x50 ,0x06 ,0x5
To implement EMV TLV data parsing you need to follow ISO 8825-1 BER-TLV encoding rules, also known as ASN.1 Basic Encoding Rules (BER).
To be short, to extract correct BER-TLV Value you need analyse Tag and Length bits to get its correct size.
Then your sample buffer will be like this:
tag: "6F"
len: "35" # // 53
val:#"8408454F504343415244A5295006555A4B4152545F2D06757A7275656E87..0B0A"
- x84:#"8408454F504343415244"
- tag: "84"
- len: "08" # // 8
- val: "454F504343415244"
- xA5:#"A5295006555A4B4152545F2D06757A7275656E8701019F1101019F120655..0B0A"
- tag: "A5"
- len: "29" # // 41
- val:#"5006555A4B4152545F2D06757A7275656E8701019F1101019F1206555A4B..0B0A"
- x50:#"5006555A4B415254"
- tag: "50"
- len: "06" # // 6
- val: "555A4B415254"
- x5F2D:#"5F2D06757A7275656E"
- tag: "5F2D"
- len: "06" # // 6
- val: "757A7275656E"
- x87:#"870101"
- tag: "87"
- len: "01" # // 1
- val: "01"
- x9F11:#"9F110101"
- tag: "9F11"
- len: "01" # // 1
- val: "01"
- x9F12:#"9F1206555A4B415254"
- tag: "9F12"
- len: "06" # // 6
- val: "555A4B415254"
- xBF0C:#"BF0C059F4D020B0A"
- tag: "BF0C"
- len: "05" # // 5
- val:#"9F4D020B0A"
- x9F4D:#"9F4D020B0A"
- tag: "9F4D"
- len: "02" # // 2
- val: "0B0A"
Or, in case you wish to apply EMV analysis:
# Cheef's parser.
# lib : "/lib/EMV/" - EMV - Integrated Circuit Card Specifications for Payment Systems
# node : "TLVs"
TLVs:#"6F358408454F504343415244A5295006555A4B4152545F2D06757A727565..0B0A" # EMV, Tag + Length + Value (TLV) series
- x6F:#"6F358408454F504343415244A5295006555A4B4152545F2D06757A727565..0B0A" # ISO 7816, Template, File Control Parameters and File Management Data (FCI)
- tag: "6F"
- len: "35" # // 53
- val:#"8408454F504343415244A5295006555A4B4152545F2D06757A7275656E87..0B0A"
- x84:#"8408454F504343415244" # EMV, Dedicated File (DF) Name
- tag: "84"
- len: "08" # // 8
- val:#"454F504343415244" # Dedicated File (DF) Name.
- RID: "454F504343" # Registered Application Provider Identifier (RID)
- PIX: "415244" # Proprietary Application Identifier Extension (PIX)
- xA5:#"A5295006555A4B4152545F2D06757A7275656E8701019F1101019F120655..0B0A" # EMV, Template, FCI A5
- tag: "A5"
- len: "29" # // 41
- val:#"5006555A4B4152545F2D06757A7275656E8701019F1101019F1206555A4B..0B0A"
- x50:#"5006555A4B415254" # ISO 7816, Application Label
- tag: "50"
- len: "06" # // 6
- val: "555A4B415254" # Application Label. // UZKART
- x5F2D:#"5F2D06757A7275656E" # ISO 7816, Language Preference
- tag: "5F2D"
- len: "06" # // 6
- val:#"757A7275656E" # Language Preference. // uzruen
- A21: "uz" # ISO 639, Language Code (a2) // Uzbek
- A22: "ru" # ISO 639, Language Code (a2) // Russian
- A23: "en" # ISO 639, Language Code (a2) // English
- x87:#"870101" # EMV, Application Priority Indicator
- tag: "87"
- len: "01" # // 1
- val:#"01" # Application Priority Indicator.
- S01: "0" # // Application may be selected without confirmation of cardholder
- S02: "1" # // No priority assigned
- x9F11:#"9F110101" # EMV, Issuer Code Table Index
- tag: "9F11"
- len: "01" # // 1
- val: "01" # Issuer Code Table Index. // ISO 8859, Part 1
- x9F12:#"9F1206555A4B415254" # EMV, Application Preferred Name
- tag: "9F12"
- len: "06" # // 6
- val: "555A4B415254" # Name, Application Preferred. // UZKART
- xBF0C:#"BF0C059F4D020B0A" # EMV, FCI Issuer Discretionary Data
- tag: "BF0C"
- len: "05" # // 5
- val:#"9F4D020B0A" # FCI Issuer Discretionary Data.
- x9F4D:#"9F4D020B0A" # EMV, Log Entry
- tag: "9F4D"
- len: "02" # // 2
- val:#"0B0A" # Log Entry.
- B01: "0B" # SFI containing the cyclic transaction log file (binary) // 11
- B02: "0A" # Maximum number of records in the transaction log file (binary) // 10
I can provide the following code to parse BER-TLV:
int parseTlv(const unsigned char *buffer, int length, void* ctx, int (*tagCallback)(void* ctx, int tag, int length, const unsigned char* value)) {
while(length>0) {
// Get tag
int tag=*(buffer++);
int tagLength,tmp;
length--;
if((tag&0x1F)==0x1F) {
if((length--)==0) return -1;
tag=(tag<<8)|*(buffer++);
if((tag&0x80)==0x80) {
if((length--)==0) return -1;
tag=(tag<<8)|*(buffer++);
if((tag&0x80)==0x80) {
if((length--)==0) return -1;
tag=(tag<<8)|*(buffer++);
if((tag&0x80)==0x80) {
// Longer tags are NOT supported
return -1;
}
}
}
} else {
if(tag==0) {
continue;
}
}
// Get length
if((length--)==0) return -1;
tmp=*(buffer++);
tagLength=0;
switch(tmp) {
case 0x84:
if((length--)==0) return -1;
tagLength=*(buffer++);
/* no break */
case 0x83:
if((length--)==0) return -1;
tagLength=(tagLength<<8)|*(buffer++);
/* no break */
case 0x82:
if((length--)==0) return -1;
tagLength=(tagLength<<8)|*(buffer++);
/* no break */
case 0x81:
if((length--)==0) return -1;
tagLength=(tagLength<<8)|*(buffer++);
break;
default:
if(tmp>=0x80) {
// Other 8x variants are NOT supported
return -1;
}
tagLength=tmp;
break;
}
// Check value
if(tagLength>length) return -1;
// Process tag
if(tagCallback(ctx, tag, tagLength, buffer)<0) {
return -1;
}
buffer+=tagLength;
length-=tagLength;
}
return 0;
}