Untagged CHOICE in ASN.1

2.2k Views Asked by At

We are using the JAC ASN.1 Compiler in our (legacy) project and experienced a situation where we received ASN.1 messages we could not parse.

I took a look at the parser code and noticed that this is a bug in the library. Since switching to a commercial solution isn't so easy, we are forced to fork the library and fix it.

TL;DR

Is there any situation during ASN.1 decoding where you read a tag number and instead of looking if your schema has this tag number on the level of the structure you are in, you look if any nested element has this tag?

Here is a mininal example reproducing the bug in the library. You can use this online playground to compile the schema etc. and see that a functioning parser has no problems with it.

Schema

Basically, it's a structure with two elements: A choice and a string, both of which are optional. The key to the example is that the choice element contains an element with a tag number equal to the tag number of the optional string.

Example DEFINITIONS ::= BEGIN

Message ::= SEQUENCE {
    params [1] EXPLICIT Params OPTIONAL,
    confuser [2] IMPLICIT PrintableString OPTIONAL
}

Params ::= CHOICE {
    unimportant [1] IMPLICIT PrintableString,
    accident [2] IMPLICIT Accident
}

Accident ::= SEQUENCE {
    irrelevant [1] IMPLICIT PrintableString OPTIONAL
}

END

Example Message

message Message ::= {
    confuser "Foo"
}

Example Message (encoded, BER)

30058203 466F6F

When using the JAC parser, it will correctly read the tag number 2 in the message and then iterates over the elements in the schema to find the element to associate it with. However, with choice elements it has some extra logic:

Instead of saying "params has tag 1, which is not 2, so this value does not correspond to it", it goes "params has tag 1, which is not 2, but it is a choice element and it contains an element with tag 2, so this has got to be the element I am looking for".

It then takes the Accident class, goes on with parsing and finally fails because it now can't find the tag 2 in this class.

A comment in the parser logic says that this is for cases of untagged choice elements. But this confuses me: According to everything I read, untagged choice elements don't have a default tag but will instead be tagged explicitly; moreover, a choice can never be tagged implicitly.

But with this information I fail to imagine any scenario where this logic in the parser is useful and I am leaning towards simply deleting it. However, I just got familiar with ASN.1 because of this bug and may be overlooking something – am I?

2

There are 2 best solutions below

3
On BEST ANSWER

There's one possibility for having to look into the tags of choice member and is that the choice is added without tags:

For example if your definition were:

Message ::= SEQUENCE {
    params Params OPTIONAL,
    confuser [2] IMPLICIT PrintableString OPTIONAL
}

Answering your question.

A comment in the parser logic says that this is for cases of untagged choice elements. But this confuses me: According to everything I read, untagged choice elements don't have a default tag but will instead be tagged explicitly; moreover, a choice can never be tagged implicitly.

No, untagged choice elements take the tag of the choice. It is tagged choice elements what must be tagged explicitly and can never be tagged implicitly.


Going back to your definition

Message ::= SEQUENCE {
    params [1] EXPLICIT Params OPTIONAL,
    confuser [2] IMPLICIT PrintableString OPTIONAL
}

the params component must have a explicit tag (both because you mark it as EXPLICIT... but also because of clause 30.7.c of X.680:

30.6 The tagging construction specifies explicit tagging if any of the following holds:

...

c. the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3).

In this scenario encoding goes:

x.690: clause 8.9.2 & 8.9.3

8.9.2 The contents octets shall consist of the complete encoding of one data value from each of the types listed in the ASN.1 definition of the sequence type, in the order of their appearance in the definition, unless the type was referenced with the keyword OPTIONAL or the keyword DEFAULT.

8.9.3 The encoding of a data value may, but need not, be present for a type which was referenced with the keyword OPTIONAL or the keyword DEFAULT. If present, it shall appear in the encoding at the point corresponding to the appearance of the type in the ASN.1 definition.

But for params component the rule that applies is first the 8.14.2

8.14.2 If implicit tagging (see ITU-T Rec. X.680 | ISO/IEC 8824-1, 30.6) was not used in the definition of the type, the encoding shall be constructed and the contents octets shall be the complete base encoding.

... and only within the explicit tagged type we can consider the encoding of the choice 8.13

8.13 Encoding of a choice value The encoding of a choice value shall be the same as the encoding of a value of the chosen type.

0
On

The real problem here is that the parser looks into nested elements in spite of the specified tag. The params field, if present, would always be tagged with [1] in the encoding.

For untagged fields it does make sense to look into the nested types for tags.