ASN.1 Utilities
Utility for reading and writing ASN.1 based content, including handling BER and
DER.
The Ups and Downs of ASN.1
There are various benefits to
Pros
- ASN.1
Cons
- The
ASN.1IDL syntax is very complicated, and there are few OSS systems to help with IDL parsing, BER encoding or decoding, code generation, etc. This OSS project is in part meant to counter that argument, and make
Comparison to ...
protobuf/grpc: ...json: ...
The ASN.1 Syntax
There is a good source of material for figuring out the ASN.1 DLS syntax, where I have among others used access to various sources of ASN.1 files on the internet, and publicly available books.
First a number of terms and what is meant by them:
identifier: Identifiers are a string of[a-zA-Z][a-zA-Z0-9]*(-[a-zA-z0-9]+)*.Type-Name: Types are identified by an identifier that starts with an upper-case letter, and indicates the name of a type, either specified throughout the ASN.1 IDL, or from theUNIVERSALnamespace. Note that in theUNIVERSALtype namespace, there are a number of types that contains a space in it, likeOCTET STRING,BIT STRINGorOBJECT IDENTIFIER. Only these universally known types may contain the space character as part of the name.value-name: Values (non-type fields and identifiers) starts with a a lower-case letter, and identified a name pointing to a value with a type.SubType: is a modification of a type, usually enclosed in(...), but sometimes not, exceptions will be mentioned later.- String literals may be:
"Double quoted for unicode string"'0100'Bfor encoding bit string, with single-quotes.'917421436587'Hfor hexadecimal encoded octet strings, with single-quites.fooor{'foo' 9 'bar'}using single quotes, or bracket enclosed sequence of single-quote strings and decimal code points forASCIIencoded strings (aka IA5 in ASN.1 spec), where the code-points can represent non-printable characters.
Integernumbers are of the form-?[1-9][0-9]*, and can represent all- It is not allowed to represent decimal form numbers (with a dot), but are defined
Each ASN.1 file is referenced as a Module.
Module-Name { module object id }
DEFINITIONS
-- add file options here
-- ( AUTOMATIC | IMPLICIT | EXPLICIT )? TAGS
BEGIN
-- put stuff here
END
Note that the ID part is not mandatory, but if it is missing, this ASN.1 spec may not be imported into other ASN.1 spec files. If EXPORTS is specified, then ONLY those types and values may be referenced outside this module, though if no EXPORTS are present, then ALL types and values may be referenced outside the module.
EXPORTS Type-Name, Type-Name2;
For any type or value not defined in the module itself, or in the UNIVERSAL type namespace
only imported IDs may be used in this module.
IMPORTS
Type-Name, value-name FROM Other-File-Spec { id }
Type-Name2 FROM Third-File-Spec { id };
Note that the ID part is mandatory when importing.
Type-Name ::= `Type-Definition`
Class-Name ::= CLASS `Class-Specification`
Type-Set `Type-Specification` ::= { v1 | v2 }
value-name ::= `Type-Specification` `value`
value-name `Type-Specification` ::= `value`
value-name `Type-Specification` ::= { `value specification` }
Where I use the term Type definition to be how to define a type, while the term Type specification is a reference to
a type with optional sub-typing. The latter one have less flexibility when it comes to what it can specify, but both of
them can take a type and modify them before using it in its relative position.
And yes, for some reason both ways of defining values are allowed (:facepalm:).
Classes
Class-Name ::= CLASS {
&Class-Type,
&class-value Value-Type
} WITH SYNTAX {
[`optional string containing one or more fields from above`]
`string containing fields from above`
`in total all fields must be referenced exactly once`
}
class-Value Class-Name ::= {
SYNTAX WORD value-name
MORE WORDS Type-Name
EVEN MORE WORDS 42
}
Class-Object-Set Class-Name ::= { class-Value | class-Value-2, ... }
-- AFAIK only SEQUENCE types may be templated in this way.
Templated-Type ::= SEQUENCE {
identifier Class-Name.&class-value({Class-Object-Set}),
argument Class-Name.&Class-Type({Class-Object-Set}{@identifier})
}
- TODO: Figure out the
ANY DEFINED BYtype. It looks like the only reasonable way to represent is is to have a TLV of unknown type, and let the implementers connect the dots.
And yes, both ways
Constructed types.
There exist 3 types of constructed types:
CHOICESEQUENCESET
Constructed-Type ::= Base-Type {
component Component-Type,
..., -- either this or ...
...! err1 err2, -- <- exception marker ?????
extended Component-Type-2 -- for version2
}
err1 INTEGER ::= 501
err2 INTEGER ::= 503
Constructed types and extensibility.
Normally constructed types have a fixed component set, but by either
adding the ... extensible-marker component, or by setting.
Choice
A choice is a meta-type that signifies the presence of at most one of a set of values.
Choice-Type ::= CHOICE {
first-choice [0] Value-Type-1,
second-choice [1] Value-Type-2
}
- Type matching a CHOICE is the same as matching any of its fields, and it is not encoded into it's own constructed group unless used in an EXPLICIT numbered field in a sequence or set.
Sequence and Set
Sequences and set's are pretty similar, but where the SEQUENCE type can take advantage of the fact that fields must come in the order they were defined.
Sub-Types and constraints
String-Type ::= UTF8String (SIZE (from..to)) -- bounded
String-Type ::= UTF8String (SIZE (from..)) -- unbounded on higher numbers
String-Type ::= UTF8String (SIZE (fixed)) -- fixed size
String-Type ::= IA5String (FROM ("A" .. "Z")) -- limited alphabet
String-Type ::= IA5String (FROM ("0" .. "9", A" .. "Z")) (SIZE (1 ..)) -- limited alphabet
Integer-Type ::= INTEGER (min..max) -- integer with bounded value.
Integer-Type ::= INTEGER (min..) -- integer with lower-bound value.
Integer-Type ::= INTEGER (..max) -- integer with upper-bound value.
Sequence-Type ::= Origin-Type (WITH COMPONENTS {..., `additional components`})
For extending a sequence with additional components: They will be inserted where the extension point is, or at the end if