# Syntax for Packing Paths Quick DER logo This specification describes the path format used by `der_unpack()` and `der_pack()` to pass through a DER binary and map it to (or from) an array of `dercursor` values. A simpler variation of a similar idea is the [WALK SYNTAX](WALK-SYNTAX.MD), which limits itself to finding a single element, but also based on a (different) walking path expression. ## Declaring and Using a Walking Path The walk path is described as a sequence of values that ends in `DER_PACK_END`: #include derwalk path_demo [] = { ..., DER_PACK_END } which is then used to unpack the DER binary data under a `dercursor crs` with int prsok; prsok = der_unpack (&crs, path_demo, outputs, 1); The `outputs` is an array of `dercursor` that will be filled with information found in the `path_demo`. Explicit storage instructions will put the information there, so the required size of the array is equal to the fixed number of such instructions in `path_demo`. ## Diving in Head-First The path described by `path_demo` should be a depth-first traversal of a *static* structure. That means that `SEQUENCE OF` and `SET OF`, the two ways of expressing dynamically sized structures in ASN.1, require special handling. To dive in, use a flag `DER_PACK_ENTER` and to leave a nested structure, use the `DER_PACK_LEAVE` flag. To store intermediate values, use `DER_PACK_STORE` as a flag, as in derwalk path_demo [] = { DER_PACK_ENTER | ..., DER_PACK_STORE | ..., ..., DER_PACK_LEAVE, DER_PACK_END } Note that `DER_PACK_LEAVE` is an instruction on its own. The other two forms are extended with a tag that is verified to match, for instance derwalk path_demo [] = { DER_PACK_ENTER | DER_TAG_SEQUENCE, DER_PACK_ENTER | DER_TAG_CONTEXT (0), DER_PACK_STORE | DER_TAG_INTEGER, DER_PACK_LEAVE, DER_PACK_STORE | DER_TAG_OCTETSTRING, DER_PACK_LEAVE, DER_PACK_END } An example ASN.1 structure that could be traversed by this would be demoStruct ::= SEQUENCE { demoCounter [0] INTEGER, demoName OCTET STRING } After invoking `der_unpack()` on this path, there are two values in the `output` array of `dercursor`, namely for the two `DER_PACK_STORE` instructions. From the following input data 30 16 -- tag SEQUENCE, length 16 a0 03 -- tag a0 for [0], length 3 02 01 07 -- tag INTEGER, length 1, value 7 04 09 51 75 69 63 6b 20 44 45 52 -- tag 4, length 9, "Quick DER" this would make `output[0]` point to the `INTEGER` value 7, with 1 byte length, and `output[1]` would point to the `OCTETSTRING` contents "Quick DER", with a length of 9 bytes. Note how nothing remains of the DER tags or lengths. This is what you should expect from a Quick and Easy DER parser. ## Dealing with Variable Structures It is possible to not store everything we encounter. In the previous situation we might have used derwalk path_demo [] = { DER_PACK_ENTER | DER_TAG_SEQUENCE, DER_PACK_STORE | DER_TAG_CONTEXT (0), DER_PACK_STORE | DER_TAG_OCTETSTRING, DER_PACK_LEAVE, DER_PACK_END } to find `output[0]` set to the DER sequence *contained in* `[0] INTEGER`, which means the `INTEGER` in DER coding, so in hex the bytes `02 01 07` instead of just to `07`. This can be useful at some times. Imagine the ASN.1 structure aFewPrimes ::= SET OF INTEGER which is a `SET OF` and can thus contain as many `INTEGER` values as desired. An example hexdump of a DER value listing the first 5 primes would be 31 0f -- SET, containing 15 bytes 02 01 02 -- INTEGER 2 02 01 03 -- INTEGER 3 02 01 05 -- INTEGER 5 02 01 07 -- INTEGER 7 02 01 0b -- INTEGER 11 If we had to store each `INTEGER` in a separate `output[]` entry, we would need a variable-sized output array. What `der_unpack()` does in these cases is the same as demonstrated for `[0] INTEGER` above; it stores the entire structure *contained inside* the `SET OF` and leaves it for further processing. The path expression to store this set would be derwalk path_primes [] = { DER_PACK_STORE | DER_TAG_SET_OF, DER_PACK_END } The result would be stored in `output[0]` as the sequence `02 01 02 ...` of length 15. It is now possible to do a few things: * use `der_iterate_first()` and `der_iterate_next()` to find the individual values in the set; * manually skip through the list with `der_skip()` until it hits the end of the set; * counting the entries with `der_countelements()` and then allocate an array `dercursor primal[5]`, in the heap or on the stack, and pass it into `der_unpack ()` with the last parameter set to the count of 5. ## Optionals, Choices and the ANYs Not everything declared in ASN.1 is included in the binary DER format. Some parts are `OPTIONAL` (and may have a `DEFAULT`, which Quick DER does not capture) and others are a `CHOICE` from variants. To encode an option, prefix `DER_PACK_OPTIONAL,` to the optional part. If the optional part is flagged wth `DER_PACK_ENTER`, then the optionality will continue to the corresponding `DER_PACK_LEAVE`. Note that ASN.1 ensures that the DER format can always be parsed based on a singly tag lookahead, which we exploit in this case. A somewhat similar structure is the `CHOICE` which permits choosing a syntax variety from among alternatives. Again, ASN.1 ensures that parsing can be done based on the first tag. We use this once more, for paths between `DER_PACK_CHOICE_BEGIN` and `DER_PACK_CHOICE_END`. Note that the programmer of the walking path is responsible for proper nesting, also with respect to `ENTER`/`LEAVE` structure. Finally, the forms `ANY` and `ANY DEFINED BY` are used in ASN.1 to describe wildcard typing. These have no representation in DER either, but at the point where it comes across the `DER_PACK_STORE | DER_PACK_ANY` instruction, it will match anything, and store the result in the output array of `dercursor`. The stored result is special however, in that it includes the entire DER structure including tag and length bytes. This is because you will have to do further processing. ## Overlay structures The idea of static structure is a great benefit to us as programmers, because we can create overlay structures that consist solely of `dercursor` and other overlay structures. These give us a way to navigate through the data using ASN.1 labels. The first ASN.1 structure demoStruct ::= SEQUENCE { demoCounter [0] INTEGER, demoName OCTET STRING } could be overlaid with the C structure typedef struct { dercursor demoCounter; dercursor demoName; } ovly_demoStruct; and the program could declare ovly_demoStruct output; and pass that to `der_unpack()` as `(dercursor *) &output` for type correctness. The datafields could then be addressed with something like printf ("Found \"%.*s\"\n", output.demoName.derlen, output.demoName.derptr); ## Repacking There is a function `der_pack()` that does the exact opposite of `der_unpack()`, using the same walking paths. *Something to ignore until you run into trouble:* You may need to `der_prepack()` first if you have nested elements that are not a `SET (OF)` or `SEQUENCE (OF)` or other form that is always Constructed. Without `der_prepack()` your DER representation may end up being Primitive. ## ASN.1 Compiler and RFC Library The can generate this syntax automatically from common ASN.1 files, including the overlay structures. The result of this is a headerfile providing macros that can fill paths, and structures that capture the structure of ASN.1 and specifically the labels used. Now we have a compiler, we have started to collect RFCs (and we may later add other specifications) that use ASN.1 syntax, and to derive their header files for distribution in the developer version of Quick and Easy DER. These things combined enable you to specify things like #include #include typedef DER_OVLY_rfc5280_Certificate Certificate; derwalk path_cert [] = { DER_PACK_rfc5280_Certificate, DER_PACK_END }; void print (dercursor *input) { Certificate crt; if (der_unpack (&input, path_cert, (dercursor *) &crt, 1) == 0) { ...crt.tbsCertificate.issuer... } } In short, you are already up and running with DER-encoded PKIX Certificates. And it will be Quick... and Easy!