include "asn1.m"; asn1 := load ASN1 ASN1->PATH; asn1->init(); Elem: adt { tag: Tag; val: ref Value; is_seq: fn(e: self ref Elem): (int, list of ref Elem); is_set: fn(e: self ref Elem): (int, list of ref Elem); is_int: fn(e: self ref Elem): (int, int); is_bigint: fn(e: self ref Elem): (int, array of byte); is_bitstring: fn(e: self ref Elem): (int, int, array of byte); is_octetstring: fn(e: self ref Elem): (int, array of byte); is_oid: fn(e: self ref Elem): (int, ref Oid); is_string: fn(e: self ref Elem): (int, string); is_time: fn(e: self ref Elem): (int, string); tostring: fn(e: self ref Elem): string; }; Tag: adt { class: int; num: int; constr: int; tostring: fn(t: self Tag): string; }; Value: adt { pick { Bool or Int => v: int; Octets or BigInt or Real or Other => bytes: array of byte; BitString => unusedbits: int; bits: array of byte; Null or EOC => ; ObjId => id: ref Oid; String => s: string; Seq or Set => l: list of ref Elem; } tostring: fn(v: self ref Value): string; }; Oid: adt { nums: array of int; tostring: fn(o: self ref Oid): string; }; init: fn(); decode: fn(a: array of byte): (string, ref Elem); decode_seq: fn(a: array of byte): (string, list of ref Elem); decode_value: fn(a: array of byte, kind, constr: int): (string, ref Value); encode: fn(e: ref Elem): (string, array of byte); oid_lookup: fn(o: ref Oid, tab: array of Oid): int; print_elem: fn(e: ref Elem);
ASN1 handles the BER encodings of all types from the ASN.1 Universal class, and provides a simple OBJECT IDENTIFIER comparison facility.
For simplicity, ASN1 does not take a description of the ASN.1 module of the data being processed. Consequently, the (de)composition of tagged types must be performed by the application. ASN1 does not know the context of tagged values and so cannot determine the underlying Universal type to be able to encode or decode the value automatically. See the section on Tagging for details on how the application should handle both implicit and explicit tagging.
All of the e.is_Type member functions test whether the specific Value pick variant of Elem.val and the ASN.1 Universal type, given by the tag, match and are of the requested form. A successful match yields the type specific data from the Value pick variant. The association of Universal types to Value pick variants is given in the section on the Value adt.
The function e.is_int succeeds for BOOLEAN and INTEGER ASN.1 types. The function e.is_string succeeds for all of the ASN.1 Universal string types.
Except for is_bitstring, each function returns a tuple of two values. The first tuple item is an integer, 1 for success, 0 for failure. The second item is the type specific data from the Value pick variant.
The following table lists each variant of the pick, indicating the ASN.1 Universal type values it represents, followed by a brief description. For each variant of the pick, v is taken to be of that particular type.
There are two types of tagging, implicit and explicit, defining the manner in which the values are encoded.
Implicitly tagged values are encoded in the same way as the underlying type, but with the tag class and number replaced by that specified.
Explicitly tagged values are encoded in a nested fashion. The outermost item bears the specified tag and its contents is the full encoding of the original value using the tag of its underlying type.
The following examples of how to decode and encode simple tagged types should make the distinction clear.
Type1 ::= INTEGER Type2 ::= [Application 2] Type1 -- Explicitly tagged Type3 ::= [3] IMPLICIT Type1 -- Implicitly taggedFor each of the types the value 16r55 will be decoded as follows:
(error, elem) := asn1->decode(data);
elem.tag.class == Universal elem.tag.num == INTEGER tagof elem.val == tagof Value.Int elem.is_int() == (1, 16r55)
elem.tag.class == Application elem.tag.num == 2 tagof elem.val == tagof Value.OctetsThe bytes array of the Value.Octets value contains the complete encoding of the Type1 value. The actual value can be obtained as follows:
pick v := elem.val { Octets => (err2, e2) := asn1->decode(v.bytes); }with e2 having exactly the same properties as elem in the Type1 case above.
elem.tag.class == Context elem.tag.num == 3 tagof elem.val == tagof Value.OctetsIn this case the bytes array of the Value.Octets value contains the encoding of just the value part of the Type1 value, not the complete encoding. The actual value can be obtained as follows:
pick v := e.val { Octets => constr := e.tag.constr; (err, val) := asn1->decode_value(v.bytes, INTEGER, constr); }Note that the application has to infer the type of the value from the context in which it occurs. The resultant val is of the type Value.Int with the value 16r55 stored in the v member variable.
tag := Tag(Universal, INTEGER, 0); val := Value.Int(16r55); elem := ref Elem(tag, val); (err, data) := asn1->encode(elem);
tag1 := Tag(Universal, INTEGER, 0); val1 := Value.Int(16r55); elem1 := ref Elem(tag1, val1); (err1, data1) := asn1->encode(elem1); tag2 := Tag(Application, 2, 0); val2 := Value.Octets(data1); elem2 := ref Elem(tag2, val2); (err, data) := asn1->encode(elem2);
tag := Tag(Context, 3, 0); val := Value.Int(16r55); elem := ref Elem(tag, val); (err, data) := asn1->encode(elem);
String encodings are converted as UTF-8 byte sequences. This will result in strings comprising any character codes above 127 being incorrectly converted.
There is a particular form of BER encoding that the module will handle incorrectly, resulting in a decoding error. The error occurs when a tagged value is encoded using the indefinite length specifier and the constructed representation.
ASN1(2 ) | Rev: Thu Feb 15 14:43:26 GMT 2007 |