eerimoq / asn1tools

ASN.1 parsing, encoding and decoding.
MIT License
288 stars 95 forks source link

Error Decoding Indefinite Length ASN.1 Elements Containing Extension Marker #183

Open aziz-marashly opened 1 week ago

aziz-marashly commented 1 week ago

I have been using this decoder for around three years. I encountered an error when decoding some files and assumed they were corrupted. However, after a long debugging session, I was able to abstract and detect the leading cause of the failure.

Description: I encountered a decoding error while using an ASN.1 decoder for elements defined with indefinite length that included an extension marker (...). This error appears specifically when decoding elements labeled with application-specific tags.

Steps to Reproduce:

This is an example asn1 definition and test code to reproduce


import base64
import asn1tools

if __name__ == "__main__":

    SPECIFICATION = """
    HelloWorld DEFINITIONS IMPLICIT TAGS ::= BEGIN
        Message ::= [APPLICATION 1]  SEQUENCE
        {
            id          [APPLICATION 2] UTF8String OPTIONAL,
            header      [APPLICATION 3] UTF8String OPTIONAL,
            content     [APPLICATION 4] UTF8String OPTIONAL,
            ...,
            other       [APPLICATION 5] UTF8String OPTIONAL
        }

    END
    """
    compiler = asn1tools.compile_string(SPECIFICATION)

    base64_message_def = "YRVCAmlkQwZoZWFkZXJEB2NvbnRlbnQ="  # defined lenght
    decoded_from_base64 = compiler.decode(
        "Message", base64.b64decode(base64_message_def.encode("ascii"))
    )
    print("defined length", decoded_from_base64)

    base64_message_ind = "YYBCAmlkQwZoZWFkZXJEB2NvbnRlbnQAAA=="  # indefinite  lenght
    decoded_from_base64 = compiler.decode(
        "Message", base64.b64decode(base64_message_ind.encode("ascii"))
    ) # exception will be raised here 
    print("indefinite length", decoded_from_base64)

Output:

Exception has occurred: OutOfByteDataError
Message: Ran out of data when trying to find End of Contents tag for indefinite length field (At offset: 25)
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 112, in detect_end_of_contents_tag
    raise OutOfByteDataError(
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 94, in is_end_of_data
    elif detect_end_of_contents_tag(data, offset):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 791, in decode_members
    out_of_data, offset = is_end_of_data(data, offset, end_offset)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 759, in decode_content
    offset, out_of_data = self.decode_members(flatten(self.additions), data, values, offset, end_offset,
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 551, in decode
    return self.decode_content(data, offset, length)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 1565, in decode_with_length
    raise e
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 1565, in decode_with_length
    raise e
  File "path\to\lib\asn1tools\asn1tools\codecs\ber.py", line 1550, in decode
    return self.decode_with_length(data)[0]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "path\to\lib\asn1tools\asn1tools\compiler.py", line 167, in decode
    decoded = type_.decode(data)
              ^^^^^^^^^^^^^^^^^^
  File "path\to\lib\asn1tools\fast_example.py", line 28, in <module>
    decoded_from_base64 = compiler.decode(
                          ^^^^^^^^^^^^^^^^
asn1tools.codecs.ber.OutOfByteDataError: Message: Ran out of data when trying to find End of Contents tag for indefinite length field (At offset: 25)

If we change the ANS1 specs and move the ... indicator to the end of the element, then everything will be fine. This example will work without any exceptions

import base64
import asn1tools

if __name__ == "__main__":

    SPECIFICATION = """
    HelloWorld DEFINITIONS IMPLICIT TAGS ::= BEGIN
        Message ::= [APPLICATION 1]  SEQUENCE
        {
            id          [APPLICATION 2] UTF8String OPTIONAL,
            header      [APPLICATION 3] UTF8String OPTIONAL,
            content     [APPLICATION 4] UTF8String OPTIONAL,
            other       [APPLICATION 5] UTF8String OPTIONAL,
            ...
        }

    END
    """
    compiler = asn1tools.compile_string(SPECIFICATION)

    base64_message_def = "YRVCAmlkQwZoZWFkZXJEB2NvbnRlbnQ="  # defined lenght
    decoded_from_base64 = compiler.decode(
        "Message", base64.b64decode(base64_message_def.encode("ascii"))
    )
    print("defined length", decoded_from_base64)

    base64_message_ind = "YYBCAmlkQwZoZWFkZXJEB2NvbnRlbnQAAA=="  # indefinite  lenght
    decoded_from_base64 = compiler.decode(
        "Message", base64.b64decode(base64_message_ind.encode("ascii"))
    )
    print("indefinite length", decoded_from_base64)

Expected output:

defined length {'id': 'id', 'header': 'header', 'content': 'content'}
indefinite length {'id': 'id', 'header': 'header', 'content': 'content'}

Is this a bug in the decoder's handling of indefinite-length elements with extension markers, or is the ASN.1 specification incorrectly interpreted by the decoder, or is the ASN.1 specification incorrect?

Context:

Thank you

aziz-marashly commented 5 days ago

I found here that it could be possible as a use-case, as mentioned here in this comment: https://sourceforge.net/p/asn1c/discussion/357921/thread/31c6b188/

MyTest ::= SEQUENCE {
  a        INTEGER,
  b        INTEGER,
  …,
  c        INTEGER OPTIONAL
  …
}