how to create APDU (TLV) parser in C

2019-02-11 03:31发布

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 ,0x55 ,0x5A ,0x4B ,0x41 ,0x52 ,0x54 ,0x5F,
    0x2D, 0x06 ,0x75 ,0x7A ,0x72 ,0x75 ,0x65 ,0x6E ,0x87 ,0x01 ,0x01 ,0x9F ,0x11 ,0x01 ,0x01 ,0x9F,   
    0x12 ,0x06 ,0x55 ,0x5A ,0x4B ,0x41 ,0x52 ,0x54 ,0xBF ,0x0C ,0x05 ,0x9F ,0x4D ,0x02 ,0x0B ,0x0A};

I want to parse this TLV in C, like on this page: parsed TLV

It should parse all tags and their value and take in structure. I tried to parse and that's what I got:

#include <stdio.h>
#include <stdlib.h>


#define uchar unsigned char
#define uint unsigned int
#define ushort unsigned short

#define k6f 0x6F

typedef enum{
    false,
    true
}bool;

struct TAG{
    uchar *data;
    uint len;
};
struct TLVdata{
    struct TAG s6F;
    struct TAG s84;
    struct TAG sA5;
    struct TAG s50;
    struct TAG s5F2D;
    struct TAG s87;
    struct TAG s9F11;
    struct TAG s9F12;
    struct TAG sBF0C;
    struct TAG s9F4D;
};

static int readTLV(uchar rApdu[]);

int main(void){
    uchar rApdu[] = {0x6F, 0x35 ,0x84 ,0x08 ,0x45 ,0x4F ,0x50 ,
        0x43 ,0x43 ,0x41 ,0x52 ,0x44 ,0xA5 ,0x29 ,0x50 ,0x06 ,0x55 ,0x5A ,0x4B ,0x41 ,0x52 ,0x54 ,0x5F,
        0x2D, 0x06 ,0x75 ,0x7A ,0x72 ,0x75 ,0x65 ,0x6E ,0x87 ,0x01 ,0x01 ,0x9F ,0x11 ,0x01 ,0x01 ,0x9F,   
        0x12 ,0x06 ,0x55 ,0x5A ,0x4B ,0x41 ,0x52 ,0x54 ,0xBF ,0x0C ,0x05 ,0x9F ,0x4D ,0x02 ,0x0B ,0x0A};
    if (readTLV(rApdu) == 0)
        printf("ok!\n");
    system("PAUSE");
    return false;
}



static int readTLV(uchar rApdu[]){
    uint i, j, aLen, tLen, tlvIndex;
    struct TLVdata newtlv;
    aLen = sizeof(rApdu);
    tlvIndex = 0;

    switch(rApdu[tlvIndex]){
        case k6f:
        tLen = (int)rApdu[++tlvIndex];
        for(i=0; i <= tLen; i++, ++tlvIndex){
            if(rApdu[tlvIndex] == 0x84){
                newtlv.s84.len = (int)rApdu[++tlvIndex];
                ++tlvIndex;
                newtlv.s84.data = (char*)malloc(10*sizeof(char));
                for(j=0; j < newtlv.s84.len; j++, ++tlvIndex){
                    newtlv.s84.data[j] = rApdu[tlvIndex];
                }
                newtlv.s84.data[j] = '\0';
                printf("%s\n", newtlv.s84.data);
            }

        }
        break;
    }

    return 0;
}

I found one parser. Only it is written in Java or do not know in what language. Can someone help? apduparser

标签: c parsing apdu
2条回答
女痞
2楼-- · 2019-02-11 04:12

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;
}
查看更多
时光不老,我们不散
3楼-- · 2019-02-11 04:28

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
查看更多
登录 后发表回答