ASN.1 DER (Abstract Syntax Notation One, Distinguished Encoding Rules): GraphViz block diagram (.dot) source

ASN.1 (Abstract Syntax Notation One) DER (Distinguished Encoding Rules) is a standard-backed serialization scheme used in many different use-cases. Particularly popular usage scenarios are X.509 certificates and some telecommunication / networking protocols.

DER is self-describing encoding scheme which allows representation of simple, atomic data elements, such as strings and numbers, and complex objects, such as sequences of other elements.

DER is a subset of BER (Basic Encoding Rules), with an emphasis on being non-ambiguous: there's always exactly one canonical way to encode a data structure defined in terms of ASN.1 using DER.

This spec allows full parsing of format syntax, but to understand the semantics, one would typically require a dictionary of Object Identifiers (OIDs), to match OID bodies against some human-readable list of constants. OIDs are covered by many different standards, so typically it's simpler to use a pre-compiled list of them, such as:

  • https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg
  • http://oid-info.com/
  • https://www.alvestrand.no/objectid/top.html

KS implementation details

License: CC0-1.0

This page hosts a formal specification of ASN.1 DER (Abstract Syntax Notation One, Distinguished Encoding Rules) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

GraphViz block diagram source

asn1_der.dot

digraph {
	rankdir=LR;
	node [shape=plaintext];
	subgraph cluster__asn1_der {
		label="Asn1Der";
		graph[style=dotted];

		asn1_der__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
			<TR><TD PORT="type_tag_pos">0</TD><TD PORT="type_tag_size">1</TD><TD>u1→TypeTag</TD><TD PORT="type_tag_type">type_tag</TD></TR>
			<TR><TD PORT="len_pos">1</TD><TD PORT="len_size">3</TD><TD>LenEncoded</TD><TD PORT="len_type">len</TD></TR>
			<TR><TD PORT="body_pos">4</TD><TD PORT="body_size">...</TD><TD>switch (type_tag)</TD><TD PORT="body_type">body</TD></TR>
		</TABLE>>];
asn1_der__seq_body_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
	<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
	<TR><TD>:type_tag_sequence_30</TD><TD PORT="case0">BodySequence</TD></TR>
	<TR><TD>:type_tag_sequence_10</TD><TD PORT="case1">BodySequence</TD></TR>
	<TR><TD>:type_tag_utf8string</TD><TD PORT="case2">BodyUtf8string</TD></TR>
	<TR><TD>:type_tag_printable_string</TD><TD PORT="case3">BodyPrintableString</TD></TR>
	<TR><TD>:type_tag_object_id</TD><TD PORT="case4">BodyObjectId</TD></TR>
	<TR><TD>:type_tag_set</TD><TD PORT="case5">BodySequence</TD></TR>
</TABLE>>];
		subgraph cluster__body_sequence {
			label="Asn1Der::BodySequence";
			graph[style=dotted];

			body_sequence__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				<TR><TD PORT="entries_pos">0</TD><TD PORT="entries_size">...</TD><TD>Asn1Der</TD><TD PORT="entries_type">entries</TD></TR>
				<TR><TD COLSPAN="4" PORT="entries__repeat">repeat to end of stream</TD></TR>
			</TABLE>>];
		}
		subgraph cluster__body_utf8string {
			label="Asn1Der::BodyUtf8string";
			graph[style=dotted];

			body_utf8string__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				<TR><TD PORT="str_pos">0</TD><TD PORT="str_size">⇲</TD><TD>str(UTF-8)</TD><TD PORT="str_type">str</TD></TR>
			</TABLE>>];
		}
		subgraph cluster__body_object_id {
			label="Asn1Der::BodyObjectId";
			graph[style=dotted];

			body_object_id__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				<TR><TD PORT="first_and_second_pos">0</TD><TD PORT="first_and_second_size">1</TD><TD>u1</TD><TD PORT="first_and_second_type">first_and_second</TD></TR>
				<TR><TD PORT="rest_pos">1</TD><TD PORT="rest_size">⇲</TD><TD></TD><TD PORT="rest_type">rest</TD></TR>
			</TABLE>>];
			body_object_id__inst__first [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
				<TR><TD>first</TD><TD>(first_and_second / 40)</TD></TR>
			</TABLE>>];
			body_object_id__inst__second [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
				<TR><TD>second</TD><TD>(first_and_second % 40)</TD></TR>
			</TABLE>>];
		}
		subgraph cluster__len_encoded {
			label="Asn1Der::LenEncoded";
			graph[style=dotted];

			len_encoded__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				<TR><TD PORT="b1_pos">0</TD><TD PORT="b1_size">1</TD><TD>u1</TD><TD PORT="b1_type">b1</TD></TR>
				<TR><TD PORT="int2_pos">1</TD><TD PORT="int2_size">2</TD><TD>u2be</TD><TD PORT="int2_type">int2</TD></TR>
			</TABLE>>];
			len_encoded__inst__result [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
				<TR><TD>result</TD><TD>((b1 &amp; 128) == 0 ? b1 : int2)</TD></TR>
			</TABLE>>];
		}
		subgraph cluster__body_printable_string {
			label="Asn1Der::BodyPrintableString";
			graph[style=dotted];

			body_printable_string__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
				<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
				<TR><TD PORT="str_pos">0</TD><TD PORT="str_size">⇲</TD><TD>str(ASCII)</TD><TD PORT="str_type">str</TD></TR>
			</TABLE>>];
		}
	}
	asn1_der__seq:len_type -> len_encoded__seq [style=bold];
	asn1_der__seq:body_type -> asn1_der__seq_body_switch [style=bold];
	asn1_der__seq_body_switch:case0 -> body_sequence__seq [style=bold];
	asn1_der__seq_body_switch:case1 -> body_sequence__seq [style=bold];
	asn1_der__seq_body_switch:case2 -> body_utf8string__seq [style=bold];
	asn1_der__seq_body_switch:case3 -> body_printable_string__seq [style=bold];
	asn1_der__seq_body_switch:case4 -> body_object_id__seq [style=bold];
	asn1_der__seq_body_switch:case5 -> body_sequence__seq [style=bold];
	asn1_der__seq:type_tag_type -> asn1_der__seq:body_type [color="#404040"];
	body_sequence__seq:entries_type -> asn1_der__seq [style=bold];
	body_object_id__seq:first_and_second_type -> body_object_id__inst__first [color="#404040"];
	body_object_id__seq:first_and_second_type -> body_object_id__inst__second [color="#404040"];
	len_encoded__seq:b1_type -> len_encoded__inst__result [color="#404040"];
	len_encoded__seq:b1_type -> len_encoded__inst__result [color="#404040"];
	len_encoded__seq:int2_type -> len_encoded__inst__result [color="#404040"];
}