The following example demonstrates the basic functionality used to run the Erlang ASN.1 compiler.
Create a file named
People DEFINITIONS AUTOMATIC TAGS ::= BEGIN Person ::= SEQUENCE { name PrintableString, location INTEGER {home(0),field(1),roving(2)}, age INTEGER OPTIONAL } END
This file must be compiled before it can be used. The ASN.1 compiler checks that the syntax is correct and that the text represents proper ASN.1 code before generating an abstract syntax tree. The code-generator then uses the abstract syntax tree to generate code.
The generated Erlang files are placed in the current directory or
in the directory specified with option
The following shows how the compiler can be called from the Erlang shell:
1> asn1ct:compile("People", [ber]). ok 2>
Option
2> asn1ct:compile("People", [ber,verbose]). Erlang ASN.1 compiling "People.asn" --{generated,"People.asn1db"}-- --{generated,"People.hrl"}-- --{generated,"People.erl"}-- ok 3>
ASN.1 module
or
Assume that there is a network
application that receives instances of the ASN.1 defined
type
receive
{Port,{data,Bytes}} ->
case 'People':decode('Person',Bytes) of
{ok,P} ->
{ok,Answer} = 'People':encode('Person',mk_answer(P)),
Port ! {self(),{command,Answer}};
{error,Reason} ->
exit({error,Reason})
end
end,
In this example, a series of bytes is received from an
external source and the bytes are then decoded into a valid
Erlang term. This was achieved with the call
The encoder and decoder can also be run from the shell:
2> Rockstar = {'Person',"Some Name",roving,50}. {'Person',"Some Name",roving,50} 3> {ok,Bin} = 'People':encode('Person',Rockstar). {ok,<<243,17,19,9,83,111,109,101,32,78,97,109,101,2,1,2, 2,1,50>>} 4> {ok,Person} = 'People':decode('Person',Bin). {ok,{'Person',"Some Name",roving,50}} 5>
It is common that ASN.1 modules import defined types, values, and other entities from another ASN.1 module.
Earlier versions of the ASN.1 compiler required that modules that were imported from had to be compiled before the module that imported. This caused problems when ASN.1 modules had circular dependencies.
Referenced modules are now parsed when the compiler finds an entity that is imported. No code is generated for the referenced module. However, the compiled modules rely on that the referenced modules are also compiled.
The
The module
The module
The reason for this division of the interfaces into compile-time
and runtime
is that only runtime modules (
The ASN.1 compiler can be started directly from the command line
by the
erlc Person.asn erlc -bper Person.asn erlc -bber ../Example.asn erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
Useful options for the ASN.1 compiler:
Choice of encoding rules. If omitted,
Where to put the generated files. Default is the current directory.
Where to search for
DER encoding rule. Only when using option
Use maps instead of records to represent the
This functionality works together with option
A buffer that holds a message being decoded can also have
trailing bytes. If those trailing bytes are important, they
can be returned along with the decoded value by compiling
the ASN.1 specification with option
Any option can be added to the Erlang compiler when compiling the generated Erlang files. Any option unrecognized by the ASN.1 compiler is passed to the Erlang compiler.
For a complete description of
The compiler and other compile-time functions can also be started
from the Erlang shell. Here follows a brief
description of the primary functions. For a
complete description of each function, see module
The compiler is started by
Example:
asn1ct:compile("H323-MESSAGES.asn1").
This equals:
asn1ct:compile("H323-MESSAGES.asn1",[ber]).
If PER encoding is wanted:
asn1ct:compile("H323-MESSAGES.asn1",[per]).
The generic encode and decode functions can be called as follows:
'H323-MESSAGES':encode('SomeChoiceType',{call,<<"octetstring">>}). 'H323-MESSAGES':decode('SomeChoiceType',Bytes).
When an ASN.1 specification is compiled with option
By calling function
Errors detected at compile-time are displayed on the screen together with line numbers indicating where in the source file the respective error was detected. If no errors are found, an Erlang ASN.1 module is created.
The runtime encoders and decoders execute within a catch and
return
Currently,
There are various reasons for using multi-file compilation:
Specify which ASN.1 specs to compile in a module with extension
File1.asn File2.asn File3.asn
If you compile with the following, the result is one merged
module
~> erlc MyModule.set.asn
Tags used to be important for all users of ASN.1, because it was necessary to add tags manually to certain constructs in order for the ASN.1 specification to be valid. Example of an old-style specification:
Tags DEFINITIONS ::= BEGIN Afters ::= CHOICE { cheese [0] IA5String, dessert [1] IA5String } END
Without the tags (the numbers in square brackets) the ASN.1 compiler refused to compile the file.
In 1994 the global tagging mode
Tags DEFINITIONS AUTOMATIC TAGS ::= BEGIN Afters ::= CHOICE { cheese IA5String, dessert IA5String } END
Tags are not mentioned any more in this User's Guide.
This section describes the ASN.1 types including their functionality, purpose, and how values are assigned in Erlang.
ASN.1 has both primitive and constructed types:
The values of each ASN.1 type have their own representation in Erlang, as described in the following sections. Users must provide these values for encoding according to the representation, as shown in the following example:
Operational ::= BOOLEAN --ASN.1 definition
In Erlang code it can look as follows:
Val = true, {ok,Bytes} = MyModule:encode('Operational', Val),
Booleans in ASN.1 express values that can be either
In ASN.1 it is possible to have:
Operational ::= BOOLEAN
Assigning a value to type
Myvar1 = true,
Thus, in Erlang the atoms
ASN.1 itself specifies indefinitely large integers. Erlang systems with version 4.3 and higher support very large integers, in practice indefinitely large integers.
The concept of subtyping can be applied to integers and to other ASN.1 types. The details of subtyping are not explained here; for more information, see X.680. Various syntaxes are allowed when defining a type as an integer:
T1 ::= INTEGER T2 ::= INTEGER (-2..7) T3 ::= INTEGER (0..MAX) T4 ::= INTEGER (0<..MAX) T5 ::= INTEGER (MIN<..-99) T6 ::= INTEGER {red(0),blue(1),white(2)}
The Erlang representation of an ASN.1
The following is an example of Erlang code that assigns values for the types in the previous list:
T1value = 0, T2value = 6, T6value1 = blue, T6value2 = 0, T6value3 = white
These Erlang variables are now bound to valid instances of ASN.1 defined types. This style of value can be passed directly to the encoder for transformation into a series of bytes.
The decoder returns an atom if the value corresponds to a
symbol in the
The following ASN.1 type is used for real numbers:
R1 ::= REAL
It is assigned a value in Erlang as follows:
R1value1 = "2.14", R1value2 = {256,10,-2},
In the last line, notice that the tuple {256,10,-2} is the real number
2.56 in a special notation, which encodes faster than simply
stating the number as
The type
Notype ::= NULL
This type is assigned in Erlang as follows:
N1 = 'NULL',
The actual value is the quoted atom
The type
DaysOfTheWeek ::= ENUMERATED { sunday(1),monday(2),tuesday(3), wednesday(4),thursday(5),friday(6),saturday(7) }
For example, to assign a weekday value in Erlang, use the same atom
as in the
Day1 = saturday,
The enumerated type is similar to an integer type, when defined with a set of predefined values. The difference is that an enumerated type can only have specified values, whereas an integer can have any value.
The type
In ASN.1,
Bits1 ::= BIT STRING Bits2 ::= BIT STRING {foo(0),bar(1),gnu(2),gnome(3),punk(14)}
The following two notations are available for representation of
Bits1Val1 = <<0:1,1:1,0:1,1:1,1:1>>, Bits2Val1 = [gnu,punk], Bits2Val2 = <<2#1110:4>>, Bits2Val3 = [bar,gnu,gnome],
Bits3 ::= BIT STRING (SIZE(0..31))
This means that no bit higher than 31 can be set.
In addition to the representations described earlier, the
following deprecated representations are available if the
specification has been compiled with option
It is possible to have the following ASN.1 type definitions:
O1 ::= OCTET STRING O2 ::= OCTET STRING (SIZE(28))
With the following example assignments in Erlang:
O1Val = <<17,13,19,20,0,0,255,254>>, O2Val = <<"must be exactly 28 chars....">>,
By default, an
ASN.1 supports a wide variety of character sets. The main difference
between an
However, when using, for example, IA5String (which closely resembles ASCII), byte 65 (in decimal notation) means character 'A'.
For example, if a defined type is to be a VideotexString and
an octet is received with the unsigned integer value
The ASN.1 to Erlang compiler does not determine the correct interpretation of each BER string octet value with different character strings. The application is responsible for interpretation of octets. Therefore, from the BER string point of view, octets are very similar to character strings and are compiled in the same way.
When PER is
used, there is a significant difference in the encoding scheme
between
Examples:
Digs ::= NumericString (SIZE(1..3)) TextFile ::= IA5String (SIZE(0..64000))
The corresponding Erlang assignments:
DigsVal1 = "456", DigsVal2 = "123", TextFileVal1 = "abc...xyz...", TextFileVal2 = [88,76,55,44,99,121 .......... a lot of characters here ....]
The Erlang representation for "BMPString" and "UniversalString" is either a list of ASCII values or a list of quadruples. The quadruple representation associates to the Unicode standard representation of characters. The ASCII characters are all represented by quadruples beginning with three zeros like {0,0,0,65} for character 'A'. When decoding a value for these strings, the result is a list of quadruples, or integers when the value is an ASCII character.
The following example shows how it works. Assume the following
specification is in file
PrimStrings DEFINITIONS AUTOMATIC TAGS ::= BEGIN BMP ::= BMPString END
Encoding and decoding some strings:
1> asn1ct:compile('PrimStrings', [ber]). ok 2> {ok,Bytes1} = 'PrimStrings':encode('BMP', [{0,0,53,53},{0,0,45,56}]). {ok,<<30,4,53,54,45,56>>} 3> 'PrimStrings':decode('BMP', Bytes1). {ok,[{0,0,53,53},{0,0,45,56}]} 4> {ok,Bytes2} = 'PrimStrings':encode('BMP', [{0,0,53,53},{0,0,0,65}]). {ok,<<30,4,53,53,0,65>>} 5> 'PrimStrings':decode('BMP', Bytes2). {ok,[{0,0,53,53},65]} 6> {ok,Bytes3} = 'PrimStrings':encode('BMP', "BMP string"). {ok,<<30,20,0,66,0,77,0,80,0,32,0,115,0,116,0,114,0,105,0,110,0,103>>} 7> 'PrimStrings':decode('BMP', Bytes3). {ok,"BMP string"}
Type UTF8String is represented as a UTF-8 encoded binary in
Erlang. Such binaries can be created directly using the binary syntax
or by converting from a list of Unicode code points using function
The following shows examples of how UTF-8 encoded binaries can be created and manipulated:
1> Gs = "Мой маленький Гном". [1052,1086,1081,32,1084,1072,1083,1077,1085,1100,1082,1080, 1081,32,1043,1085,1086,1084] 2> Gbin = unicode:characters_to_binary(Gs). <<208,156,208,190,208,185,32,208,188,208,176,208,187,208, 181,208,189,209,140,208,186,208,184,208,185,32,208,147, 208,...>> 3> Gbin = <<"Мой маленький Гном"/utf8>>. <<208,156,208,190,208,185,32,208,188,208,176,208,187,208, 181,208,189,209,140,208,186,208,184,208,185,32,208,147, 208,...>> 4> Gs = unicode:characters_to_list(Gbin). [1052,1086,1081,32,1084,1072,1083,1077,1085,1100,1082,1080, 1081,32,1043,1085,1086,1084]
For details, see the
In the following example, this ASN.1 specification is used:
UTF DEFINITIONS AUTOMATIC TAGS ::= BEGIN UTF ::= UTF8String END
Encoding and decoding a string with Unicode characters:
5> asn1ct:compile('UTF', [ber]). ok 6> {ok,Bytes1} = 'UTF':encode('UTF', <<"Гном"/utf8>>). {ok,<<12,8,208,147,208,189,208,190,208,188>>} 7> {ok,Bin1} = 'UTF':decode('UTF', Bytes1). {ok,<<208,147,208,189,208,190,208,188>>} 8> io:format("~ts\n", [Bin1]). Гном ok 9> unicode:characters_to_list(Bin1). [1043,1085,1086,1084]
The type
Oid ::= OBJECT IDENTIFIER
Therefore, the following example is a valid Erlang instance of type 'Oid':
OidVal1 = {1,2,55},
The
The first value is limited to the values 0, 1, or 2. The second value must be in the range 0..39 when the first value is 0 or 1.
The
Values of this type can be assigned a value as an ordinary string as follows:
"This is the value of an Object descriptor"
Two time types are defined within ASN.1: Generalized Time and Universal Time Coordinated (UTC). Both are assigned a value as an ordinary string within double quotes, for example, "19820102070533.8".
For DER encoding, the compiler does not check the validity of the time values. The DER requirements upon those strings are regarded as a matter for the application to fulfill.
The structured types of ASN.1 are constructed from other types in a manner similar to the concepts of array and struct in C.
A
Pdu ::= SEQUENCE { a INTEGER, b REAL, c OBJECT IDENTIFIER, d NULL }
This is a 4-component structure called
-record('Pdu',{a, b, c, d}).
The record declarations for a module
Values can be assigned in Erlang as follows:
MyPdu = #'Pdu'{a=22,b=77.99,c={0,1,2,3,4},d='NULL'}.
The decode functions return a record as result when decoding
a
A
An application can use the atom
Depending on the encoding rules, the encoder can also compare the given value to the default value and automatically omit the encoding if the values are equal. How much effort the encoder makes to compare the values depends on the encoding rules. The DER encoding rules forbid encoding a value equal to the default value, so it has a more thorough and time-consuming comparison than the encoders for the other encoding rules.
In the following example, this ASN.1 specification is used:
File DEFINITIONS AUTOMATIC TAGS ::= BEGIN Seq1 ::= SEQUENCE { a INTEGER DEFAULT 1, b Seq2 DEFAULT {aa TRUE, bb 15} } Seq2 ::= SEQUENCE { aa BOOLEAN, bb INTEGER } Seq3 ::= SEQUENCE { bs BIT STRING {a(0), b(1), c(2)} DEFAULT {a, c} } END
Example where the BER encoder is able to omit encoding of the default values:
1> asn1ct:compile('File', [ber]). ok 2> 'File':encode('Seq1', {'Seq1',asn1_DEFAULT,asn1_DEFAULT}). {ok,<<48,0>>} 3> 'File':encode('Seq1', {'Seq1',1,{'Seq2',true,15}}). {ok,<<48,0>>}
Example with a named
4> 'File':encode('Seq3', {'Seq3',asn1_DEFAULT). {ok,<<48,0>>} 5> 'File':encode('Seq3', {'Seq3',<<16#101:3>>). {ok,<<48,4,128,2,5,160>>}
The DER encoder omits the encoding for the same
6> asn1ct:compile('File', [ber,der]). ok 7> 'File':encode('Seq3', {'Seq3',asn1_DEFAULT). {ok,<<48,0>>} 8> 'File':encode('Seq3', {'Seq3',<<16#101:3>>). {ok,<<48,0>>}
In Erlang, the
When a
SExt ::= SEQUENCE { a INTEGER, ..., b BOOLEAN }
In this case it has got a new
component
The component
During decoding, the
If the ASN.1 module has been compiled with option
In the following example, this ASN.1 specification is used:
File DEFINITIONS AUTOMATIC TAGS ::= BEGIN Seq1 ::= SEQUENCE { a INTEGER DEFAULT 42, b BOOLEAN OPTIONAL, c IA5String } END
Optional fields are to be omitted from the map if they have no value:
1> asn1ct:compile('File', [per,maps]). ok 2> {ok,E} = 'File':encode('Seq1', #{a=>0,c=>"string"}). {ok,<<128,1,0,6,115,116,114,105,110,103>>}
When decoding, optional fields will be omitted from the map:
3> 'File':decode('Seq1', E). {ok,#{a => 0,c => "string"}}
Default values can be omitted from the map:
4> {ok,E2} = 'File':encode('Seq1', #{c=>"string"}). {ok,<<0,6,115,116,114,105,110,103>>} 5> 'File':decode('Seq1', E2). {ok,#{a => 42,c => "string"}}
It is not allowed to use the atoms
The type
Assume the following:
SomeModuleName DEFINITIONS AUTOMATIC TAGS ::= BEGIN T ::= CHOICE { x REAL, y INTEGER, z OBJECT IDENTIFIER } END
It is then possible to assign values as follows:
TVal1 = {y,17}, TVal2 = {z,{0,1,2}},
A
When a
{asn1_ExtAlt, BytesForOpenType}
Here
The types
Arr1 ::= SET SIZE (5) OF INTEGER (4..9) Arr2 ::= SEQUENCE OF OCTET STRING
In Erlang the following can apply:
Arr1Val = [4,5,6,7,8], Arr2Val = ["abc",[14,34,54],"Octets"],
Notice that the definition of type
However, for a value of type
The types
A value of this type is encoded as an
Instead of
See also
The types
The type
The structured types previously described can have other named
types as their components. The general syntax to assign a value
to component
EmbeddedExample DEFINITIONS AUTOMATIC TAGS ::= BEGIN B ::= SEQUENCE { a Arr1, b T } Arr1 ::= SET SIZE (5) OF INTEGER (4..9) T ::= CHOICE { x REAL, y INTEGER, z OBJECT IDENTIFIER } END
1> 'EmbeddedExample':encode('B', {'B',[4,5,6,7,8],{x,"7.77"}}). {ok,<<5,56,0,8,3,55,55,55,46,69,45,50>>}
When the option
When an ASN.1 specification is compiled, all defined types of type
Some special cases of this functionality are presented in the next section.
In ASN.1 it is also possible to have components that are themselves structured types. For example, it is possible to have the following:
Emb ::= SEQUENCE { a SEQUENCE OF OCTET STRING, b SET { a INTEGER, b INTEGER DEFAULT 66}, c CHOICE { a INTEGER, b FooType } } FooType ::= [3] VisibleString
The following records are generated because of type
-record('Emb,{a, b, c}). -record('Emb_b',{a, b = asn1_DEFAULT}). % the embedded SET type
Values of type
V = #'Emb'{a=["qqqq",[1,2,255]],
b = #'Emb_b'{a=99},
c ={b,"Can you see this"}}.
For an embedded type of type
Example:
Seq ::= SEQUENCE{ a CHOICE{ b SEQUENCE { c INTEGER } } }
This results in the following record:
-record('Seq_a_b',{c}).
If the structured type has a component with an embedded
Seq ::= SEQUENCE { a SEQUENCE OF SEQUENCE { b } c SET OF SEQUENCE { d } }
This results in the following records:
-record('Seq_a_SEQOF'{b}). -record('Seq_c_SETOF'{d}).
A parameterized type is to be considered as an embedded
type. Each time such a type is referenced, an instance of it is
defined. Thus, in the following example a record with name
Seq ::= SEQUENCE { b PType{INTEGER} } PType{T} ::= SEQUENCE{ id T }
Types that refer to themselves are called recursive types. Example:
Rec ::= CHOICE { nothing NULL, something SEQUENCE { a INTEGER, b OCTET STRING, c Rec }}
This is allowed in ASN.1 and the ASN.1-to-Erlang compiler supports this recursive type. A value for this type is assigned in Erlang as follows:
V = {something,#'Rec_something'{a = 77, b = "some octets here", c = {nothing,'NULL'}}}.
Values can be assigned to an ASN.1 type within the ASN.1 code itself, as opposed to the actions in the previous section where a value was assigned to an ASN.1 type in Erlang. The full value syntax of ASN.1 is supported and X.680 describes in detail how to assign values in ASN.1. A short example:
TT ::= SEQUENCE { a INTEGER, b SET OF OCTET STRING } tt TT ::= {a 77,b {"kalle","kula"}}
The value defined here can be used in several ways. It can, for
example, be used as the value in some
SS ::= SET { s OBJECT IDENTIFIER, val TT DEFAULT tt }
It can also be used from inside an Erlang program. If this ASN.1
code is defined in ASN.1 module
1> Val = 'Values':tt(). {'TT',77,["kalle","kula"]} 2> {ok,Bytes} = 'Values':encode('TT',Val). {ok,<<48,18,128,1,77,161,13,4,5,107,97,108,108,101,4,4, 107,117,108,97>>} 4> 'Values':decode('TT',Bytes). {ok,{'TT',77,["kalle","kula"]}} 5>
This example shows that a function is generated by the compiler that returns a valid Erlang representation of the value, although the value is of a complex type.
Furthermore, if the option
The type
Information Object Classes, Information Objects, and Information Object Sets (in the following called classes, objects, and object sets, respectively) are defined in the standard definition X.681. Only a brief explanation is given here.
These constructs makes it possible to define open types, that is, values of that type can be of any ASN.1 type. Also, relationships can be defined between different types and values, as classes can hold types, values, objects, object sets, and other classes in their fields. A class can be defined in ASN.1 as follows:
GENERAL-PROCEDURE ::= CLASS { &Message, &Reply OPTIONAL, &Error OPTIONAL, &id PrintableString UNIQUE } WITH SYNTAX { NEW MESSAGE &Message [REPLY &Reply] [ERROR &Error] ADDRESS &id }
An object is an instance of a class. An object set is a set containing objects of a specified class. A definition can look as follows:
object1 GENERAL-PROCEDURE ::= { NEW MESSAGE PrintableString ADDRESS "home" } object2 GENERAL-PROCEDURE ::= { NEW MESSAGE INTEGER ERROR INTEGER ADDRESS "remote" }
The object
GENERAL-PROCEDURES GENERAL-PROCEDURE ::= { object1 | object2}
You cannot encode a class, object, or object set, only refer to it when defining other ASN.1 entities. Typically you refer to a class as well as to object sets by table constraints and component relation constraints (X.682) in ASN.1 types, as in the following:
StartMessage ::= SEQUENCE { msgId GENERAL-PROCEDURE.&id ({GENERAL-PROCEDURES}), content GENERAL-PROCEDURE.&Message ({GENERAL-PROCEDURES}{@msgId}), }
In type
So, the value
In practice, object sets are usually declared to be extensible so that more objects can be added to the set later. Extensibility is indicated as follows:
GENERAL-PROCEDURES GENERAL-PROCEDURE ::= { object1 | object2, ...}
When decoding a type that uses an extensible set constraint,
it is always possible that the value in field
{asn1_OPENTYPE,Binary}
Here
Parameterization, which is defined in X.683, can be used when
defining types, values, value sets, classes, objects, or object sets.
A part of a definition can be supplied as a parameter. For
example, if a
When many types (or another ASN.1 entity) only differ in some minor cases, but the structure of the types is similar, only one general type can be defined and the differences can be supplied through parameters.
Example of use of parameterization:
General{Type} ::= SEQUENCE { number INTEGER, string Type } T1 ::= General{PrintableString} T2 ::= General{BIT STRING}
An example of a value that can be encoded as type
Notice that the compiler does not generate encode/decode functions
for parameterized types, only for the instances of the parameterized
types. Therefore, if a file contains the types