How to use the NSS ASN.1 and QuickDER decoders

NSS Technical Note: 1

NSS 3.6 contains several decoders for ASN.1 and DER.Two of them are extensively used and are part of the public NSS API :
  1. The "classic" ASN.1 decoder, written by Lisa Repka . This was written to be a generic decoder, that includes both DER (Distinguished Encoding Rules) and BER (Basic Encoding Rules).† It handles both streaming and non-streaming input.
  2. The "QuickDER" decoder, written by Julien Pierre for NSS 3.6 . This decoder was written when performance issues were discovered with the classic decoder. It can only decode DER .† It does not handle streaming input, and requires that all input be present before beginning to decode.
Despite their differences, the two decoders have a lot in common. QuickDER was written to be as compatible as possible with the classic decoder, in order to ease migration to it in areas of critical performance bottlenecks. For this reason, I will first describe all the common functionality of the two decoders, before outlining their differences.

The main non-streaming APIs for these two decoders have an identical prototype :

Here is a description of the arguments : Decoder templates :

The SEC_ASN1Template structure tells the decoder what to do with the input data. This structure contains four fields : Here is a description of the various tags and modifiers that apply to the kind field.

ASN.1 tags

ASN.1 tags are specified in the lower byte of the kind field of the template, as noted above.
The following is not an attempt to explain ASN.1 tags or their purposes. Rather, the goal here is to explain what type of tags the decoder supports and which macros should be used when defining tags in decoder templates. It should be noted that we only support an older specification of ASN.1; multibyte tags are not currently supported.

The 8-bit ASN.1 tags that we support are made of three parts :

  1. The ASN.1 component class type. It is specified in the upper 2 tag bits (number 6 and 7). There are four classes of ASN.1 tags : universal, application-specific, context-specific, and private. You can specify the class of the tag using the macros SEC_ASN1_UNIVERSAL, SEC_ASN1_APPLICATION, SEC_ASN1_CONTEXT_SPECIFIC and SEC_ASN1_PRIVATE. Universal is the default tag class and does not have to be specified, as the value of the class type is zero.
  2. The method type : whether the component type is constructed or primitive. This information is stored in the next lowest tag bit (number 5). You can use the macro SEC_ASN1_CONSTRUCTED for a constructed component type. A SEC_ASN1_PRIMITIVE macro is also provided, but does not need to be included as it is zero.
  3. The tag number. It is stored in the lower 5 tag bits (number 0 through 4). The ASN.1 standard only defines tag numbers in the universal class. If you are using a tag of a different classes, you can define your own tag number macros or specify the tag value within the template definition. The following macros are provided for tag numbers within the universal class :
    SEC_ASN1_BOOLEAN, SEC_ASN1_INTEGER, SEC_ASN1_BIT_STRING, SEC_ASN1_OCTET_STRING, SEC_ASN1_NULL, SEC_ASN1_OBJECT_ID, SEC_ASN1_OBJECT_DESCRIPTOR,† SEC_ASN1_REAL, SEC_ASN1_ENUMERATED, SEC_ASN1_EMBEDDED_PDV, SEC_ASN1_UTF8_STRING, SEC_ASN1_SEQUENCE, SEC_ASN1_SET, SEC_ASN1_NUMERIC_STRING, SEC_ASN1_PRINTABLE_STRING, SEC_ASN1_T61_STRING, SEC_ASN1_TELETEX_STRING, SEC_ASN1_T61_STRING, SEC_ASN1_VIDEOTEX_STRING, SEC_ASN1_IA5_STRING, SEC_ASN1_UTC_TIME, SEC_ASN1_GENERALIZED_TIME, SEC_ASN1_GRAPHIC_STRING, SEC_ASN1_VISIBLE_STRING, SEC_ASN1_GENERAL_STRING, SEC_ASN1_UNIVERSAL_STRING, SEC_ASN1_BMP_STRING

    Note that for SEC_ASN1_SET and SEC_ASN1_SEQUENCE types, you must also include the method type macro SEC_ASN1_CONSTRUCTED to construct a fully valid tag, as defined by the ASN.1 standard .

Decoder modifiers :

These modifiers are also specified in the kind field of the template structure. All the values are in the 9 - 31 bit range.


Differences between SEC_ASN1DecodeItem and SEC_QuickDERDecodeItem

  1. The arena argument is required to be non-NULL for SEC_QuickDERDecodeItem . With SEC_ASN1DecodeItem, it can be NULL, and if so, the decoder will allocate from the heap using PR_Malloc . However, this usage is strongly discouraged and we recommend that you always use an arena pool even with SEC_ASN1DecodeItem. See bug 175163 for more information about the reason for this recommendation.
  2. SEC_ASN1DecodeItem will make a copy of the input data into the decoded target as needed, while SEC_QuickDERDecodeItem will generate output with pointers into the input. This means that if you use SEC_QuickDERDecodeItem, you must always be careful not to free the input as long as you intend to use the decoded structure. Ideally, you should allocate the input data out of the same arena that you are passing to the decoder. This will allow you to free both the input data and the decoded data at once when freeing the arena.
  3. SEC_ASN1DecodeItem can decode both BER and DER data, while SEC_QuickDERDecodeItem can only decode DER data.
  4. SEC_QuickDERDecodeItem does not support streaming data. This feature will most likely never be added, as this decoder gets most of its extra speed from not making a copy of the input data, which would be required when streaming.
  5. SEC_QuickDERDecodeItem supports SEC_ASN1_OPTIONAL together with SEC_ASN1_SKIP
  6. SEC_ASN1_DEBUG_BREAK is not supported by SEC_ASN1DecodeItem