archive about

How to create a simple ASN.1 parser

Step by step instructions to create a ASN.1 parser using asn1c.

ASN.1 is a formal notation used for describing data transmitted by telecommunications protocols, regardless of language implementation and physical representation of these data, whatever the application, whether complex or very simple. —Introduction to ASN.1

There are multiple data encodings developed for ASN.1. The most widely used ones are BER (Basic Encoding Rules), CER (Canonical-), DER (Distinguished Encoding Rules), PER (Packed Encoding Rules) and XER (XML Encoding Rules). —ASN.1 Basics

Working with ASN.1 files is not trivial, because there are only a few good tools and libraries for it, and most of them are commercial (and quite expensive).

Personally, I had never heard of ASN.1 before, until I joined ChannelVAS, and I found out that it's widely used in the telco world. Our solutions that integrate with telco's backends are able to consume ASN.1 files, but as a Solution Architect I needed a reliable stand-alone tool to encode/decode ASN.1 files during the design phase.

This article describes how to create a ASN.1 parser using asn1c.

Step 1: The ASN.1 description file.

The ASN.1 description file is a plain text file describing how the data you are interested in is structured. It will look something like this (extract taken from RFC 3525):

MEDIA-GATEWAY-CONTROL DEFINITIONS AUTOMATIC TAGS::=
   BEGIN

   MegacoMessage ::= SEQUENCE
   {
      authHeader     AuthenticationHeader OPTIONAL,
      mess           Message
   }

   AuthenticationHeader ::= SEQUENCE
   {
      secParmIndex   SecurityParmIndex,
      seqNum         SequenceNum,
      ad             AuthData
   }
  ...

So ask the party that generates the ASN.1 encoded files to provide you with the description file. You can not do much without it.

For the rest of the steps I will assume that file is named my-types.asn1 and has the following contents:

MyModule DEFINITIONS ::=
BEGIN

MyTypes ::= SEQUENCE {
    myObjectId   OBJECT IDENTIFIER,
    mySeqOf      SEQUENCE OF MyInt,
    myBitString  BIT STRING {
                        muxToken(0),
                        modemToken(1)
                 }
}

MyInt ::= INTEGER (0..65535)

END

Place my-types.asn1 in a folder without anything else in it.

Step 2: Install asn1c

Download and install asn1c. The installation instructions describe how to compile from source, but you may also find it pre-compiled or bundled for your platform. For example, if you have Homebrew on macOS, you can use brew install asn1c.

Step 3: Generate the source of your parser

cd to the folder containing my-types.asn1 and type

asn1c -E my-types.asn1

This will parse your asn1 file and print out the tree.

If you didn't get an error in the previous command, type

asn1c my-types.asn1

This will generate a number of .c and .h files, and also a file named Makefile.am.sample. Copy it to Makefile and open it with a text editor.

Scroll down towards the end to find a line like this:

TARGET = progname

and change it to whatever you want your parser to be called. For example

TARGET = mytype_converter

Also, find the line that looks like this:

CFLAGS += -I.

and change it to

CFLAGS += -DASN_CONVERTER_TITLE="MyType decoder" -DJUNKTEST -DPDU=MyTypes -I.

You can set anything you like in ASN_CONVERTER_TITLE. But make sure that PDU is set to the base type of your ASN.1 file.

Step 4: Compile your parser

This is easy. Type make and wait. If everything goes well, you should have a binary called mytype_converter (the value you set TARGET to in Step 3).

Step 5: Use it.

Type ./mytype_converter -h and you should see something like this:

$ ./mytype_converter -h
MyType decoder
Usage: ./mytype_converter <data.ber> ...
Where options are:
  -iber        Input is in BER (Basic Encoding Rules) (DEFAULT)
  -ixer        Input is in XER (XML Encoding Rules)
  -oder        Output in DER (Distinguished Encoding Rules)
  -oxer        Output in XER (XML Encoding Rules) (DEFAULT)
  -otext       Output in plain semi-structured text (dump)
  -onull       Verify (decode) input, but do not output
  -1           Decode only the first PDU in file
  -b <size>    Set the i/o buffer size (default is 8192)
  -c           Check ASN.1 constraints after decoding
  -d           Enable debugging (-dd is even better)
  -n <num>     Process files <num> times
  -s <size>    Set the stack usage limit (default is 30000)
  -J <prob>    Set random junk test bit garbaging probability

Now you can convert your asn1-encoded files to "text" or XML. For example:

./mytype_converter -otext -iber data1.ber > data1.txt

or

./mytype_converter -oxer -iber data1.ber > data1.xml


I'm a Solutions Architect at ChannelVAS, a premium Fintech provider of Mobile Financial and Value Added Services for Mobile Operators. We are hiring!