scapy icon indicating copy to clipboard operation
scapy copied to clipboard

Make a more ASN1-friendly CHOICE field (Enhancement)

Open slrdc opened this issue 6 years ago • 1 comments

This issue is a proposal to make a different ASN1 choice field type, say ASN1F_NAMEDCHOICE, that looks like a sequence. With this, choices from ASN1 definitions can be more easily translated to scapy, and a choice definition can be reused more easily in several packets/sequences.

Like a sequence, each alternative of the choice will be named. But only one value will be built when the field is expressed, and only one field will be created when the choice is dissected. Also, the value of the choice can be the last alternative that was assigned to any member.

Example borrowed from CMIP:

ObjectClass ::= CHOICE {
  ocglobalForm  [0] IMPLICIT OBJECT IDENTIFIER,
  oclocalForm   [1] IMPLICIT INTEGER
}

GetArgument ::= SEQUENCE {
  baseManagedObjectClass     ObjectClass,
  baseManagedObjectInstance  ObjectInstance,
  ...
}

GetResult ::= SEQUENCE {
  managedObjectClass     ObjectClass OPTIONAL,
  managedObjectInstance  ObjectInstance OPTIONAL,
  currentTime            [5] IMPLICIT GeneralizedTime OPTIONAL,
  ...
}

This is used in several sequences in this standard. The current syntax is clunky because each ASN1F_CHOICE instance must be named when it is defined rather than used, which results in extra names and encapsulations. Also the current ASN1F_CHOICE alternatives are class types which make it an additional burden when implicit or explicit tags are specified: one must create extra classes when the standard ASN1 field constructors have kwargs for them. Additionally, (I think) it's better to specify the name of the alternative which is being used in a standard.

With the syntax in this ticket proposal:

class ObjectClass(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_NAMEDCHOICE(
        ASN1F_OID("ocglobalForm", None, implicit_tag=0x80),
        ASN1F_INTEGER("oclocalForm", None, implicit_tag=0x81))

class GetArgument(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_SEQUENCE(
        ASN1F_PACKET("baseManagedObjectClass", ObjectClass(), ObjectClass),
        ASN1F_PACKET("baseManagedObjectInstance", ObjectInstance(), ObjectInstance),
    ....

class GetResult(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_SEQUENCE(
        ASN1F_optional(ASN1F_PACKET("managedObjectClass", None, ObjectClass)),
        ASN1F_optional(ASN1F_PACKET("managedObjectInstance", None, ObjectInstance)),
        ASN1F_optional(ASN1F_GENERALIZED_TIME("currentTime", None, implicit_tag=0x85)),
    ....

Some hybrid of the sequence and choice does almost everything this new field type should; adapting them to become a "named choice" requires a bit more work/constraint to make it express only one alternative in encoding/decoding (basically iterate through the list alternatives and stop after one works) and not have a tag of its own.

slrdc avatar Jul 22 '19 19:07 slrdc