1. Data model for ASN.1 types
All ASN.1 types could be categorized into two groups: scalar (also called simple or primitive) and constructed. The first group is populated by well-known types like Integer or String. Members of constructed group hold other types (simple or constructed) as their inner components, thus they are semantically close to a programming language records or lists.
In pyasn1, all ASN.1 types and values are implemented as Python objects. The same pyasn1 object can represent either ASN.1 type and/or value depending of the presense of value initializer on object instantiation. We will further refer to these as pyasn1 type object versus pyasn1 value object.
Primitive ASN.1 types are implemented as immutable scalar objects. There values could be used just like corresponding native Python values (integers, strings/bytes etc) and freely mixed with them in expressions.
It would be an error to perform an operation on a pyasn1 type object as it holds no value to deal with:
1.1 Scalar types
In the sub-sections that follow we will explain pyasn1 mapping to those primitive ASN.1 types. Both, ASN.1 notation and corresponding pyasn1 syntax will be given in each case.
1.1.1 Boolean type
This is the simplest type those values could be either True or False.
And here's pyasn1 version of it:
1.1.2 Null type
The NULL type is sometimes used to express the absense of any information.
We will explain the CHOICE type later in this paper, meanwhile the NULL type:
1.1.3 Integer type
ASN.1 defines the values of Integer type as negative or positive of whatever length. This definition plays nicely with Python as the latter places no limit on Integers. However, some ASN.1 implementations may impose certain limits of integer value ranges. Keep that in mind when designing new data structures.
A rather strigntforward mapping into pyasn1:
ASN.1 allows to assign human-friendly names to particular values of an INTEGER type.
The Temperature type expressed in pyasn1:
These values labels have no effect on Integer type operations, any value still could be assigned to a type (information on value constraints will follow further in this paper).
1.1.4 Enumerated type
ASN.1 Enumerated type differs from an Integer type in a number of ways. Most important is that its instance can only hold a value that belongs to a set of values specified on type declaration.
When constructing Enumerated type we will use two pyasn1 features: values labels (as mentioned above) and value constraint (will be described in more details later on).
Particular integer values associated with Enumerated value states have no meaning. They should not be used as such or in any kind of math operation. Those integer values are only used by codecs to transfer state from one entity to another.
1.1.5 Real type
Values of the Real type are a three-component tuple of mantissa, base and exponent. All three are integers.
Corresponding pyasn1 objects can be initialized with either a three-component tuple or a Python float. Infinite values could be expressed in a way, compatible with Python float type.
If a Real object is initialized from a Python float or yielded by a math operation, the base is set to decimal 10 (what affects encoding).
1.1.6 Bit string type
ASN.1 BIT STRING type holds opaque binary data of an arbitrarily length. A BIT STRING value could be initialized by either a binary (base 2) or hex (base 16) value.
The pyasn1 BitString objects can initialize from native ASN.1 notation (base 2 or base 16 strings) or from a Python tuple of binary components.
Another BIT STRING initialization method supported by ASN.1 notation is to specify only 1-th bits along with their human-friendly label and bit offset relative to the beginning of the bit string. With this method, all not explicitly mentioned bits are doomed to be zeros.
To express this in pyasn1, we will employ the named values feature (as with Enumeration type).
The BitString objects mimic the properties of Python tuple type in part of immutable sequence object protocol support.
1.1.7 OctetString type
The OCTET STRING type is a confusing subject. According to ASN.1 specification, this type is similar to BIT STRING, the major difference is that the former operates in 8-bit chunks of data. What is important to note, is that OCTET STRING was NOT designed to handle text strings - the standard provides many other types specialized for text content. For that reason, ASN.1 forbids to initialize OCTET STRING values with "quoted text strings", only binary or hex initializers, similar to BIT STRING ones, are allowed.
However, ASN.1 users (e.g. protocols designers) seem to ignore the original purpose of the OCTET STRING type - they used it for handling all kinds of data, including text strings.
In pyasn1, we have taken a liberal approach and allowed both BIT STRING style and quoted text initializers for the OctetString objects. To avoid possible collisions, quoted text is the default initialization syntax.
Most frequent usage of the OctetString class is to instantiate it with a text string.
OctetString objects support the immutable sequence object protocol. In other words, they behave like Python 3 bytes (or Python 2 strings).
When running pyasn1 on Python 3, it's better to use the bytes objects for OctetString instantiation, as it's more reliable and efficient.
Additionally, OctetString's can also be instantiated with a sequence of 8-bit integers (ASCII codes).
It is sometimes convenient to express OctetString instances as 8-bit characters (Python 3 bytes or Python 2 strings) or 8-bit integers.
1.1.8 ObjectIdentifier type
Values of the OBJECT IDENTIFIER type are sequences of integers that could be used to identify virtually anything in the world. Various ASN.1-based protocols employ OBJECT IDENTIFIERs for their own identification needs.
One of the natural ways to map OBJECT IDENTIFIER type into a Python one is to use Python tuples of integers. So this approach is taken by pyasn1.
A more human-friendly "dotted" notation is also supported.
Symbolic names of the arcs of object identifier, sometimes present in ASN.1 specifications, are not preserved and used in pyasn1 objects.
The ObjectIdentifier objects mimic the properties of Python tuple type in part of immutable sequence object protocol support.
1.1.9 Character string types
ASN.1 standard introduces a diverse set of text-specific types. All of them were designed to handle various types of characters. Some of these types seem be obsolete nowdays, as their target technologies are gone. Another issue to be aware of is that raw OCTET STRING type is sometimes used in practice by ASN.1 users instead of specialized character string types, despite explicit prohibition imposed by ASN.1 specification.
The two types are specific to ASN.1 are NumericString and PrintableString.
Their pyasn1 implementations are:
The following types came to ASN.1 from ISO standards on character sets.
The last three types are relatively recent addition to the family of character string types: UniversalString, BMPString, UTF8String.
In pyasn1, all character type objects behave like Python strings. None of them is currently constrained in terms of valid alphabet so it's up to the data source to keep an eye on data validation for these types.
1.1.10 Useful types
There are three so-called useful types defined in the standard: ObjectDescriptor, GeneralizedTime, UTCTime. They all are subtypes of GraphicString or VisibleString types therefore useful types are character string types.
It's advised by the ASN.1 standard to have an instance of ObjectDescriptor type holding a human-readable description of corresponding instance of OBJECT IDENTIFIER type. There are no formal linkage between these instances and provision for ObjectDescriptor uniqueness in the standard.
GeneralizedTime and UTCTime types are designed to hold a human-readable timestamp in a universal and unambiguous form. The former provides more flexibility in notation while the latter is more strict but has Y2K issues.
Despite their intended use, these types possess no special, time-related, handling in pyasn1. They are just printable strings.