evsinev / ber-tlv

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

Limite depth on BerTlv find #22

Open milcode opened 2 years ago

milcode commented 2 years ago

Current BerTlv find is searching for the TAG recursively and sometimes ends up returns incorrect values.

Example:

If I have the following data and want to find the BerTLV 0x80 under 0x62, it will return the 0x80 inside 0xA5.

62 19 82 02 4121 83 02 2F05 A5 03 800161 8A 01 05 8B 03 2F0605 80 02 000A

One option would be to create a findOnChildBerTlv, to limit the search to a 1 level depth. Adding a new parameter maxDepth would be another solution.

ChenShayZeak commented 2 years ago

i fixed this bug, you can copy the code in class BerTlvParser, find function "parseWithResult" and "addChildren",add params "resultLevel":

private ParseResult parseWithResult(int resultLevel,int aLevel, byte[] aBuf, int aOffset, int aLen) {
        String levelPadding = createLevelPadding(aLevel);
        if(aOffset+aLen > aBuf.length) {
            throw new IllegalStateException("Length is out of the range [offset="+aOffset+",  len="+aLen+", array.length="+aBuf.length+", level="+aLevel+"]");
        }
        if(log.isDebugEnabled()) {
            log.debug("{}parseWithResult(level={}, offset={}, len={}, buf={})", levelPadding, aLevel, aOffset, aLen, HexUtil.toFormattedHexString(aBuf, aOffset, aLen));
        }

        // tag
        int tagBytesCount = getTagBytesCount(aBuf, aOffset);
        BerTag tag        = createTag(levelPadding, aBuf, aOffset, tagBytesCount);
        if(log.isDebugEnabled()) {
            log.debug("{}tag = {}, tagBytesCount={}, tagBuf={}", levelPadding, tag, tagBytesCount, HexUtil.toFormattedHexString(aBuf, aOffset, tagBytesCount));
        }

        // length
        int lengthBytesCount  = getLengthBytesCount(aBuf, aOffset + tagBytesCount);
        int valueLength       = getDataLength(aBuf, aOffset + tagBytesCount);

        if(log.isDebugEnabled()) {
            log.debug("{}lenBytesCount = {}, len = {}, lenBuf = {}"
                    , levelPadding, lengthBytesCount, valueLength, HexUtil.toFormattedHexString(aBuf, aOffset + tagBytesCount, lengthBytesCount));
        }

        // value
        if(tag.isConstructed() && resultLevel > aLevel) {

            ArrayList<BerTlv> list = new ArrayList<BerTlv>();
            addChildren(resultLevel,aLevel, aBuf, aOffset + tagBytesCount + lengthBytesCount, levelPadding, lengthBytesCount, valueLength, list);

            int resultOffset = aOffset + tagBytesCount + lengthBytesCount + valueLength;
            if(log.isDebugEnabled()) {
                log.debug("{}returning constructed offset = {}", levelPadding, resultOffset);
            }
            return new ParseResult(new BerTlv(tag, list), resultOffset);
        } else {
            // value
            byte[] value = new byte[valueLength];
            System.arraycopy(aBuf, aOffset+tagBytesCount+lengthBytesCount, value, 0, valueLength);
            int resultOffset = aOffset + tagBytesCount + lengthBytesCount + valueLength;
            if(log.isDebugEnabled()) {
                log.debug("{}value = {}", levelPadding, HexUtil.toFormattedHexString(value));
                log.debug("{}returning primitive offset = {}", levelPadding, resultOffset);
            }
            return new ParseResult(new BerTlv(tag, value), resultOffset);
        }

    }
 private void addChildren(int resultLevel,int aLevel, byte[] aBuf, int aOffset, String levelPadding, int aDataBytesCount, int valueLength, ArrayList<BerTlv> list) {
        int startPosition = aOffset;
        int len = valueLength;
        while (startPosition < aOffset + valueLength ) {
            ParseResult result = parseWithResult(resultLevel,aLevel+1, aBuf, startPosition, len);
            list.add(result.tlv);

            startPosition = result.offset;
            len           = (aOffset + valueLength) - startPosition;

            if(log.isDebugEnabled()) {
                log.debug("{}level {}: adding {} with offset {}, startPosition={}, aDataBytesCount={}, valueLength={}"
                        , levelPadding, aLevel, result.tlv.getTag(), result.offset, startPosition, aDataBytesCount, valueLength);
            }
        }
    }

Then edit "parse" function:

   public BerTlvs parse(int resultLevel, byte[] aBuf) {
        return parse(resultLevel, aBuf, 0, aBuf.length);
    }

call it (i use it in kotlin):

 val data = "62 19\n" +
            "82 02 4121\n" +
            "83 02 2F05\n" +
            "A5 03 800161\n" +
            "8A 01 05\n" +
            "8B 03 2F0605\n" +
            "80 02 000A"
   val data2 = data.replace("\n","").replace(" ","")

    val parse = BerTlvParser().parse(1,HexUtil.parseHex(data2)).find(BerTag(0x62))
    println( parse.bytesValue)

this is result: image

when your call it: BerTlvParser().parse(0,HexUtil.parseHex(data2)).find(BerTag(0x62)) result is: image