| ASN1(2) | System Calls Manual | ASN1(2) |
asn1: decode, encode - ASN.1 (X.208), BER (X.209) encoding
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 supports decoding and encoding of the ASN.1 Basic Encoding Rules (BER, ITU-T Recommendation X.209). Despite its name, the module is not a parser for Abstract Syntax Notation One (ASN.1, ITU-T Recommendation X.208).
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.
decode_value(a, kind,
constr)
Convert the encoding of a single item value to a Value data
structure.
This is the principal data structure, representing the value of an ASN.1 item. The adt couples a data representation, the Value, with its type specifier, the Tag.
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 Tag adt denotes the ASN.1 type of a Value instance.
This pick adt provides the representation for values of each of the various Universal class types. Values of all other classes are represented by the Value.Octets branch of the pick.
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.
The Oid adt provides the value representation for OBJECT IDENTIFERs. Within the ASN.1 notation OBJECT IDENTIFIERs ultimately map to an ordered list of INTEGERs.
Tagging is an ASN.1 mechanism for disambiguating values. It is usually applied to component types, where several components of a structured type have the same underlying Universal class type. Tagging allows the client application to determine to which item of the structured type a value instance belongs.
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.
Consider the following ASN.1 type definitions:
For each of the types the value 16r55 will be decoded as follows:
Type1 ::= INTEGER
Type2 ::= [Application 2] Type1 -- Explicitly tagged
Type3 ::= [3] IMPLICIT Type1 -- Implicitly tagged
(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.Octets
The 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.Octets
In 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.To encode the value 16r55 in each of the above types, the following data structures are required.
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);
/appl/lib/asn1.b
It is irritating that REAL values are not converted by the module. This forces the application to do the conversion to and from the raw BER encoding. Fortunately they are rarely used.
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.