evsinev / ber-tlv

BER-TLV parser and builder
Apache License 2.0
126 stars 53 forks source link

FF tag #11

Open igorvlassov opened 5 years ago

igorvlassov commented 5 years ago

Dear Sirs,

I just trying to parse Visa CDET cards from https://play.google.com/store/apps/details?id=com.visa.app.cdet app. App's card 09 is marked as Card with '00' and 'FF' Padding. When READ RECORD command performed: 00b2011400 Response from the card is: 700C5F340101FFFFFF9F570208409000 ber-tlv parser parses that string in the following manner: BerTlvs{tlvs=[BerTlv{theTag=+ 70, theValue=null, theList=[BerTlv{theTag=- 5F34, theValue=[1], theList=null}, BerTlv{theTag=+ FFFFFF9F57, theValue=null, theList=[]}]}]} But according Annex B of EMV 4.3 Book 3 value where bytes from 5 to 1 all = 1 mean "see subsequent bytes" ie all FF's should be ignored. Could you fix this please? Thanks in advance, Igor

evsinev commented 5 years ago

Hi Igor,

Thank you for your report.

It seems that 700C5F340101FFFFFF9F570208409000 is a Response APDU.

[R-APDU] 9000
  [70 (response template)] 5F340101FFFFFF9F57020840
    [5F34 (PAN sequence number)] 01
    [FFFFFF9F57 (?)] 0840
      [08 (?)] 

This response contains the private constructed tag 'FFFFFF9F57' with the value length 2. It contains value '0840' which we should parse as a TLV object. But it does not look like a valid TLV structure.

May be there are any rules regarding tags beginning with ‘FF’ ?

igorvlassov commented 5 years ago

As I could see from https://www.emvco.com/wp-content/uploads/2017/05/EMV_v4.3_Book_3_Application_Specification_20120607062110791.pdf Annex B1 is when tag numbers ≥ 31 are used (that is, bits b5 - b1 of the first byte equal '11111'), subsequent bytes should be processed. Ie at least first FF should be ignored and processing should be move to the next byte. But it is unclear is the next byte is new first byte or no. Based on context Visa uses for test card I mentioned earlier I could guess they wait FF skip.

evsinev commented 5 years ago

Igor,

The library processes subsequent bytes with 0b11111. The problem is with the tag FFFFFF9F57 and its value 0840. In the 'Annex B' they say that FF at the beginning means constructed private class tag. So we need to parse '0840' as TLV not as a value.

If we change BerTag code to this:

    public boolean isConstructed() {
        for(int i=0; i<bytes.length; i++) {
            if(bytes[i] == (byte)0xff) {
                continue;
            }
            return (bytes[i] & 0x20) != 0;
        }

        throw new IllegalStateException("No bytes or all bytes are 0xff");
    }

The test will pass.

But it's not correct because we need to process FFFFFF9F57 as constructed not primitive.

igorvlassov commented 5 years ago

FF: 11 - private class, 1 - Constructed data object, 11111 - See subsequent bytes. So could we treat this in the following manner:

FF
  ↳ FF
     ↳ FF
        ↳ 9F57 [length][data]

?

evsinev commented 5 years ago

Because FF is a Constructed data object we need to process its data '0840' as another TLV object. But '0840' does not look like valid TLV structure.

igorvlassov commented 5 years ago

Yes, it looks like regular data

ushering-it commented 3 years ago

Hi, guys.

It looks like BerTlv is not as simple as it could be :) We also faced up with the problem -- and those bytes should be just skipped. The bad thing is that 00 and FF are able to be before, after, or between normal tags %)

Look, what I've found: "ISO 7816 Annex D.1: BER-TLV data object" -- https://cardwerk.com/iso7816-4-annex-d-use-of-basic-encoding-rules-asn-1/

NOTE – Before, between or after BER-TLV data objects, ’00’ or ‘FF’ bytes without any meaning may occur (e.g. due to erased or modified TLV-coded data objects).

FYI, another team also do things to work out the issue: https://github.com/Shopify/grizzly_ber/issues/13

Could you add fix for this?

Example of APDU (From VISA CDET card 9): 700C5F340101FFFFFF9F570208409000

IonutNegru87 commented 1 year ago

I encountered the same issue with the '00'. Any fix for this? It seems it crashes from:

private ParseResult parseWithResult(int aLevel, byte[] aBuf, int aOffset, int aLen) {
     ...
     int lengthBytesCount  = getLengthBytesCount(aBuf, aOffset + tagBytesCount); 
     ...
}