1 # Syntax for Packing Paths
3 <img alt="Quick DER logo" src="quick-der-logo.png" style="float: right;"/>
5 This specification describes the path format used by `der_unpack()` and
6 `der_pack()` to pass through a DER binary and map it to (or from) an array
9 A simpler variation of a similar idea is the
10 [WALK SYNTAX](WALK-SYNTAX.MD),
11 which limits itself to finding a single element, but also based on a
12 (different) walking path expression.
14 ## Declaring and Using a Walking Path
16 The walk path is described as a sequence of values that ends in `DER_PACK_END`:
18 #include <quick-der/api.h>
20 derwalk path_demo [] = {
25 which is then used to unpack the DER binary data under a `dercursor crs` with
28 prsok = der_unpack (&crs, path_demo, outputs, 1);
30 The `outputs` is an array of `dercursor` that will be filled with information
31 found in the `path_demo`. Explicit storage instructions will put the information
32 there, so the required size of the array is equal to the fixed number of such
33 instructions in `path_demo`.
35 ## Diving in Head-First
37 The path described by `path_demo` should be a depth-first traversal of a
38 *static* structure. That means that `SEQUENCE OF` and `SET OF`, the two ways
39 of expressing dynamically sized structures in ASN.1, require special handling.
41 To dive in, use a flag `DER_PACK_ENTER` and to leave a nested structure, use
42 the `DER_PACK_LEAVE` flag. To store intermediate values, use `DER_PACK_STORE`
45 derwalk path_demo [] = {
53 Note that `DER_PACK_LEAVE` is an instruction on its own. The other two
54 forms are extended with a tag that is verified to match, for instance
56 derwalk path_demo [] = {
57 DER_PACK_ENTER | DER_TAG_SEQUENCE,
58 DER_PACK_ENTER | DER_TAG_CONTEXT (0),
59 DER_PACK_STORE | DER_TAG_INTEGER,
61 DER_PACK_STORE | DER_TAG_OCTETSTRING,
66 An example ASN.1 structure that could be traversed by this would be
68 demoStruct ::= SEQUENCE {
69 demoCounter [0] INTEGER,
73 After invoking `der_unpack()` on this path, there are two values in the `output`
74 array of `dercursor`, namely for the two `DER_PACK_STORE` instructions. From the
77 30 16 -- tag SEQUENCE, length 16
78 a0 03 -- tag a0 for [0], length 3
79 02 01 07 -- tag INTEGER, length 1, value 7
80 04 09 51 75 69 63 6b 20 44 45 52 -- tag 4, length 9, "Quick DER"
82 this would make `output[0]` point to the `INTEGER` value 7, with 1 byte length,
83 and `output[1]` would point to the `OCTETSTRING` contents "Quick DER", with a
86 Note how nothing remains of the DER tags or lengths. This is what you should
87 expect from a Quick and Easy DER parser.
89 ## Dealing with Variable Structures
91 It is possible to not store everything we encounter. In the previous situation
94 derwalk path_demo [] = {
95 DER_PACK_ENTER | DER_TAG_SEQUENCE,
96 DER_PACK_STORE | DER_TAG_CONTEXT (0),
97 DER_PACK_STORE | DER_TAG_OCTETSTRING,
102 to find `output[0]` set to the DER sequence *contained in* `[0] INTEGER`,
103 which means the `INTEGER` in DER coding, so in hex
104 the bytes `02 01 07` instead of just to `07`. This can be useful at
107 Imagine the ASN.1 structure
109 aFewPrimes ::= SET OF INTEGER
111 which is a `SET OF` and can thus contain as many `INTEGER` values as desired.
112 An example hexdump of a DER value listing the first 5 primes would be
114 31 0f -- SET, containing 15 bytes
115 02 01 02 -- INTEGER 2
116 02 01 03 -- INTEGER 3
117 02 01 05 -- INTEGER 5
118 02 01 07 -- INTEGER 7
119 02 01 0b -- INTEGER 11
121 If we had to store each `INTEGER` in a separate `output[]` entry, we would need
122 a variable-sized output array. What `der_unpack()` does in these cases is the
123 same as demonstrated for `[0] INTEGER` above; it stores the entire structure
124 *contained inside* the `SET OF` and leaves it for further processing.
126 The path expression to store this set would be
128 derwalk path_primes [] = {
129 DER_PACK_STORE | DER_TAG_SET_OF,
133 The result would be stored in `output[0]` as the sequence `02 01 02 ...`
134 of length 15. It is now possible to do a few things:
136 * use `der_iterate_first()` and `der_iterate_next()` to find the individual
138 * manually skip through the list with `der_skip()` until it hits the end of
140 * counting the entries with `der_countelements()` and then allocate an array
141 `dercursor primal[5]`, in the heap or on the stack, and pass it into
142 `der_unpack ()` with the last parameter set to the count of 5.
145 ## Optionals, Choices and the ANYs
147 Not everything declared in ASN.1 is included in the binary DER format.
148 Some parts are `OPTIONAL` (and may have a `DEFAULT`, which Quick DER does not
149 capture) and others are a `CHOICE` from variants.
151 To encode an option, prefix `DER_PACK_OPTIONAL,` to the optional part. If the
152 optional part is flagged wth `DER_PACK_ENTER`, then the optionality will
153 continue to the corresponding `DER_PACK_LEAVE`. Note that ASN.1 ensures that
154 the DER format can always be parsed based on a singly tag lookahead, which we
155 exploit in this case.
157 A somewhat similar structure is the `CHOICE` which permits choosing a syntax
158 variety from among alternatives. Again, ASN.1 ensures that parsing can be done
159 based on the first tag. We use this once more, for paths between
160 `DER_PACK_CHOICE_BEGIN` and `DER_PACK_CHOICE_END`. Note that the programmer
161 of the walking path is responsible for proper nesting, also with respect to
162 `ENTER`/`LEAVE` structure.
164 Finally, the forms `ANY` and `ANY DEFINED BY` are used in ASN.1 to describe
165 wildcard typing. These have no representation in DER either, but at the
166 point where it comes across the `DER_PACK_STORE | DER_PACK_ANY` instruction,
167 it will match anything, and store the result in the output array of
168 `dercursor`. The stored result is special however, in that it includes the
169 entire DER structure including tag and length bytes. This is because you
170 will have to do further processing.
173 ## Overlay structures
175 The idea of static structure is a great benefit to us as programmers, because
176 we can create overlay structures that consist solely of `dercursor` and other
177 overlay structures. These give us a way to navigate through the data using
180 The first ASN.1 structure
182 demoStruct ::= SEQUENCE {
183 demoCounter [0] INTEGER,
184 demoName OCTET STRING
187 could be overlaid with the C structure
190 dercursor demoCounter;
194 and the program could declare
196 ovly_demoStruct output;
198 and pass that to `der_unpack()` as `(dercursor *) &output` for type correctness.
200 The datafields could then be addressed with something like
202 printf ("Found \"%.*s\"\n", output.demoName.derlen, output.demoName.derptr);
207 There is a function `der_pack()` that does the exact opposite of `der_unpack()`,
208 using the same walking paths.
210 *Something to ignore until you run into trouble:*
211 You may need to `der_prepack()` first if you have nested elements that are not
212 a `SET (OF)` or `SEQUENCE (OF)` or other form that is always Constructed.
213 Without `der_prepack()` your DER representation may end up being Primitive.
216 ## ASN.1 Compiler and RFC Library
218 The can generate this syntax automatically from common ASN.1 files, including
219 the overlay structures. The result of this is a headerfile providing macros
220 that can fill paths, and structures that capture the structure of ASN.1 and
221 specifically the labels used.
223 Now we have a compiler, we have started to collect RFCs (and we may later add
224 other specifications) that use ASN.1 syntax, and to derive their header files
225 for distribution in the developer version of Quick and Easy DER.
227 These things combined enable you to specify things like
229 #include <quick-der/api.h>
230 #include <quick-der/rfc5280.h>
232 typedef DER_OVLY_rfc5280_Certificate Certificate;
233 derwalk path_cert [] = { DER_PACK_rfc5280_Certificate, DER_PACK_END };
235 void print (dercursor *input) {
237 if (der_unpack (&input, path_cert, (dercursor *) &crt, 1) == 0) {
238 ...crt.tbsCertificate.issuer...
242 In short, you are already up and running with DER-encoded PKIX Certificates.