ASN.1/DER

Module Functionality
Provides a decoder for Abstract Syntax Notation One (ASN.1) encoding. This module allows programs to decode keyfiles using Distinguished Encoding Rules (DER), a serialization of ASN.1 standardized for cryptography.

ASN.1 encoding uses a series of tag-length-data pairs. The value may sometimes encapsulate other similarly encoded objects. For example, take a look at the PKCS#8 format for an RSA public key as well as some corresponding hexdump output:

PublicKeyInfo ::= SEQUENCE {
  algorithm ::= SEQUENCE {
    algorithm   OBJECT IDENTIFIER,
    parameters  ANY DEFINED BY algorithm OPTIONAL
  }
  PublicKey   BIT STRING
}
30 81 9f
   30 0d
      06 09   2a 86 48 86 f7 0d 01 01 01
    05 00
  03 81 8d    00 30 81 89 02 81 81 00 c0 3c a0 1c  0b 0e be b0 64 62 fc 2e 0e 8d 04 9d
              c1 a7 c7 ce 88 8d 85 87 6a 41 93 45  25 23 25 38 74 ce 4f f1 46 f5 3b 94
              19 b2 1d 6d fc a0 46 04 64 c6 b2 33  77 2f b9 89 33 6a ce 84 8a 5a ff 88
              1f 03 38 31 1d e6 08 dd d0 ae 86 fd  f5 d9 25 4f 82 1c 93 a4 cc 32 22 67
              a2 16 68 b9 d6 ae e4 b2 ee 80 93 b1  4a 2b 80 27 27 fd 99 18 90 b6 e2 97
              2a 14 51 02 ca 73 36 41 52 18 dc a8  e8 69 44 09 02 03 01 00 01

Note how in some of the tag-length-data groups there is a prefix byte of 0x81 (or similar) between the tag and the data length that seems to do nothing. It is actually a serialization of the size word. For a data size of more than 128 bytes a signed byte prefixes the size word indicating the length of the size word. If the size would require three (3) bytes, for example, then the byte 0x83 would prefix it instead.

Enumerations

enum cryptx_asn1_tags

Values:

enumerator ASN1_RESVD = 0

RESERVED.

enumerator ASN1_BOOLEAN

defines a BOOLEAN object

enumerator ASN1_INTEGER

defines an INTEGER object

enumerator ASN1_BITSTRING

defines a BIT STRING object

enumerator ASN1_OCTETSTRING

defines an OCTET STRING object

enumerator ASN1_NULL

defines a NULL object (0 size, no data)

enumerator ASN1_OBJECTID

defines an OBJECT IDENTIFIER

enumerator ASN1_OBJECTDESC

defines an OBJECT DESCRIPTION

enumerator ASN1_INSTANCE

defines an INSTANCE

enumerator ASN1_REAL

defines a REAL object

enumerator ASN1_ENUMERATED
enumerator ASN1_EMBEDDEDPDV
enumerator ASN1_UTF8STRING
enumerator ASN1_RELATIVEOID
enumerator ASN1_SEQUENCE = 16

defines a SEQUENCE

enumerator ASN1_SET

defines a SET

enumerator ASN1_NUMERICSTRING
enumerator ASN1_PRINTABLESTRING
enumerator ASN1_TELETEXSTRING
enumerator ASN1_VIDEOTEXSTRING
enumerator ASN1_IA5STRING
enumerator ASN1_UTCTIME
enumerator ASN1_GENERALIZEDTIME
enumerator ASN1_GRAPHICSTRING
enumerator ASN1_VISIBLESTRING
enumerator ASN1_GENERALSTRING
enumerator ASN1_UNIVERSALSTRING
enumerator ASN1_CHARSTRING
enumerator ASN1_BMPSTRING
enum cryptx_asn1_classes

Values:

enumerator ASN1_UNIVERSAL

tags defined in the ASN.1 standard.

Most use cases on calc will be this.

enumerator ASN1_APPLICATION

tags unique to a particular application.

enumerator ASN1_CONTEXTSPEC

tags that need to be identified within a particular, well-definded context.

enumerator ASN1_PRIVATE

reserved for use by a specific entity for their applications.

enum cryptx_asn1_forms

Values:

enumerator ASN1_PRIMITIVE

this element should contain no nested elements.

enumerator ASN1_CONSTRUCTED

this element contains nested elements.

Macros

The ASN.1 tag is an octet consisting of three (3) parts, (1) A 5-bit tag value which is one of the items in the cryptx_asn1_tags enum above, (2) A 2-bit class value which is one of the items in the cryptx_asn1_classes enum, and (3) a 1-bit form indicating if the item is constructed [encapsulates other encoded elements] or primitive [contains no encapsulated data].

cryptx_asn1_gettag(tag)

Returns the unmasked tag. See cryptx_asn1_tags above.

cryptx_asn1_getclass(tag)

Returns the 2-bit tag class flag. See cryptx_asn1_classes above.

cryptx_asn1_getform(tag)

Returns the 1-bit tag form (1 = constructed, 0 = primitive). See cryptx_asn1_forms above.

Structures

struct cryptx_asn1_object

A struct into which cryptx_asn1_decode returns metadata.

Public Members

uint8_t tag

The masked tag value of the object.

size_t len

The length of raw data encoded by the object.

uint8_t *data

Pointer to the raw data encoded by the object.

Response Codes

enum asn1_error_t

Defines error codes returned from calls to the ASN.1 API.

Values:

enumerator ASN1_OK

No errors occured.

enumerator ASN1_END_OF_FILE

End of ASN.1 data stream reached.

Technically not an error.

enumerator ASN1_INVALID_ARG

One or more arguments invalid.

enumerator ASN1_LEN_OVERFLOW

Length of an element overflowed arch size_t allowance.

Remainder of data stream unparsable.

Functions

asn1_error_t cryptx_asn1_decode(void *parse_begin, size_t parse_len, uint8_t index, struct cryptx_asn1_object *object)

Decodes the ASN.1 data at the given address.

Seeks to an element from the front of the data.

Note

Function does not recurse the ASN.1 tree structure automatically. It parses all elements at the tree level determined by parse_begin and parse_len. You may recurse manually by checking the value of cryptx_asn1_getform() on the last returned object->tag and then calling cryptx_asn1_decode() with object->data as parse_begin and object->len as parse_len.

Note

If you are using this function to decode a PKCS#8 structure for use with this library, use the PKCS#8 wrapper module instead.

Parameters
  • parse_begin – Pointer to a block of ASN.1-encoded data to parse.

  • parse_len – Length of ASN.1-encoded block to parse.

  • index – Return index-th encoded element of current tree level.

  • object – Pointer to an asn1_object to populate.

Returns

An asn1_error_t indicating the status of the operation. If index is past the end of the data, ASN1_END_OF_FILE is returned.

Here is a simple example of how to loop each element in an ASN.1 structure and return its metadata. Note how a return value of ASN1_END_OF_FILE is used as a limiter. Also notice how recursion is achieved.

void decode_level(uint8_t *data, size_t len){
  cryptx_asn1_object obj;
  uint8_t index = 0
  asn1_error_t err = ASN1_OK;
  do {
    err = cryptx_asn1_decode(data, len, index++, &obj);
    if(err == ASN1_OK){
      printf("element -- tag:%u, len:%u, data:%p\n", obj.tag, obj.len, obj.data);
      if(cryptx_asn1_getform(obj.tag))  // is a constructed object
        decode_level(obj.data, obj.len);
    }
    else {
      printf("error code: %u", err);
      break;
    }
  } while(err != ASN1_END_OF_FILE);
}

int main(void){
  // assume `asn1_data` is some imported data encoded with ASN.1
  decode_level(asn1_data, sizeof(asn1_data));
}