Merge pull request #8 from leenaars/patch-1
[quick-der] / USING.MD
1 # Quick DER parsing support
2
3 <img alt="Quick DER logo" src="quick-der-logo.png" style="float: right;"/>
4
5 > *Quick DER parsing aims to get you started with the parsing of DER (and most
6 > BER) encoded ASN.1 data really quickly.  It also aims to makes quick parsers,
7 > by using shared data structures whenever possible.*
8
9 ## Outline of using this library
10
11 Working with the quick DER library is really quick to learn.
12
13 ### Preparing your build environment
14
15 The first thing you do, is parse an ASN.1 specification that you may have gotten
16 from any source.  You map it to a header file and a
17 parser specification file:
18
19     asn2quickder myspec.asn1
20     # constructs myspec.h
21
22 Your source code dealing with DER should read the entire block, and pass it to
23 the DER parser.  Initially, it would include:
24
25     #include <quick-der/api.h>
26     #include "myderparser.h"
27
28 and builing should include:
29
30     gcc -c -o myparser.o myparser.c
31     gcc ... myparser.o -lquickder
32
33 Or when you have `pkg-config` installed, you could use
34
35     gcc `pkg-config quick-der` -c -o myparser.o myparser.c
36     gcc ... myparser.o `pkg-config quick-der --libs`
37
38 This path would be needed for your private ASN.1 specifications, but we do
39 in fact strive to include header files for common standards, including RFCs,
40 as well as ITU and ARPA2 specifications.  (If you need to do the work on any
41 of these, please send us a patch to include it in future development packages
42 of Quick DER!)  Specifications that have been included can be used simply as
43
44     #include <quick-der/api.h>
45     #include <quick-der/rfc5280.h>
46
47 and compile/link with:
48
49     gcc `pkg-config quick-der --cflags` ...
50     gcc ... `pkg-config quick-der --libs`
51
52
53 ### Parsing DER structures
54
55 Before you get to parse DER-encoded structures that match the ASN.1 syntax,
56 you should read the entire data into memory.  The parser output will not
57 clone bits and pieces of data, but instead point into it with cursors; these
58 are little structures with a pointer and a length.  Note that this means that
59 strings are not NUL-terminated; printing them may be different than what you
60 are accustomed to:
61
62     printf ("%s\n", derelem->ptr);                  // BAD: Unbounded string
63     printf ("%.*s\n", derelem->len, derelem->ptr);  // GOOD:  Bounded print
64
65 Now, to invoke the parser, you setup a cursor describing the entire content,
66
67     dercursor_t thelot;
68     thelot.derptr = ...pointer-to-data...;
69     thelot.derlen = ...length-of-data...;
70
71 then you invoke the parser, providing it with storage space and the
72 precompiled structure to follow while parsing:
73
74     struct pkix_Certificate crt;
75     int prsok = der_unpack (&thelot, asn1_pkix_Certificate, &crt, 1);
76
77 This will parse the DER-encoded data in `thelot` and store the various fields
78 in `crt`, so it becomes available as individual cursor structures such as
79 `crt.tbsCertificate.validity.notAfter`.  This follows the structure of the
80 ASN.1 syntax, and uses field names defined in it, to gradually move into
81 the structure.  The header file defines those names as part of the
82 `asn1_pkix_Certificate`.
83
84 Something else that can now be done, is switch behaviour based on the the
85 various fields that contain an `OBJECT IDENTIFIER` for that purposes.  These
86 can usually be treated as binary settings to be compared as binaries.  The
87 `dercmp()` utility does this by looking at the length as well as binary
88 contents of such fields, as in
89
90     if (dercmp (&crt.signatureAlgorith.algorithm, RSA_WITH_SHA1) == 0) {
91        ...the OIDs matched...
92     } else ...other cases...
93
94
95 ## Iterating over repeating structures
96
97 Many structures in ASN.1 are variable in the sizes of primitive data types, but
98 have a fixed composition structure.  And `OPTIONAL` parts can be parsed and their
99 respective structure fields set to NULL when they are absent.  This also happens
100 to values setup with a `DEFAULT` value in ASN.1 (note that their default value
101 is not filled in by the parser).
102
103 But some structures are not parsed immediately, because they might have a
104 repeated structure, and thus won't fit into a structure; not if the assumption
105 is that dynamic allocation should not be done by the parser.  For such
106 repeating structure, there are two options, namely iterating over their
107 contents or allocating memory and having them filled by the parser.
108
109 To iterate over a repeated structure, simply use the two routines setup for
110 that in quick DER, as in:
111
112     if (der_iterate_first (&crt.tbsCertificate.extensions.packed, &iter)) do {
113        ...treat extension pointed to by iter...
114     } while (der_iterate_next (&crt.tbsCertificate.extensions.packed, &iter));
115
116 This requires no dynamic allocation, and simply handles each of the extensions
117 in a certificate one by one.
118
119 ## Allocating space for a repeating structure
120
121 The structures that repeat are limited to the ASN.1 constructs
122 `SEQUENCE OF` and `SET OF`.  When these occur, the parser will not unfold
123 the contained structure, but simply store the whole structure.  We will
124 refer to that as "packed" representation, meaning the binary DER format.
125
126 It is possible to replace packed notation by unpacked, by assigning to it
127 an array of suitable size to contain the required number of elements,
128 and then unfold the repeated structure into it:
129
130     size_t count = der_countelements (&crt.tbsCertificate.extensions.packed);
131     pkix_Extensions *exts = calloc (count, sizeof (pkcix_Extensions));
132     if (exts === NULL) {
133        ...handle error...
134     }
135     prsok = der_unpack (&crt.tbsCertificate.extensions.packed, asn1_pkix_Extension, exts, count);
136
137 This will unpack `count` times the structure described by `asn1_pkix_Extension` and place the output in `count` structures in the array `exts`; note that the earlier
138 call the `der_unpack` had a parameter `1` in the position of `count`.
139
140 When successful, the `der_unpack()` routine replaces the `extensions.packed`
141 structure, which is a plain `dercursor_t`, with an unpacked structure
142 `unpacked` which has elements `derray` pointing to an array of cursors and
143 an element `dercnt` with the number of cursors in that array.  When this
144 is setup, the `.packed` version of the data is destroyed; the `.packed` and
145 `.unpacked` versions are in fact labels of a union.
146
147 Note that structures such as `crt` may hold a lot of useful naming, but they
148 are just a cleverly constructed overlay form for an array of `dercursor_t` fields,
149 which is exactly how `der_unpack` treats them.  The ASN.1 parsing instructions
150 are matched to the structures so that no data will be sticking out of these
151 array-like structures.
152
153 ## Composing DER output
154
155 The composition of DER output uses the same ASN.1 structural descriptions as
156 the unpacking process.  It is possible to use `.packed` structures, but once
157 they are unpacked it becomes necessary to prepare repeating structures for
158 repackaging.  This uses the `der_prepack()` function:
159
160     int prsok = der_prepack (TODO);
161
162 This sets up a third flavour of the repeated structure, namely `.prepacked`.
163 In this form, the `derlen` value has been set to the eventual length of
164 the to-be-formed DER structure, but the `derray` value still points to the
165 array of `dercursor_t` holding the to-be-filled data.  This `derlen` field
166 can subsequently be used during the future packing process.
167
168 TODO: How to distinguish packed, unpacked and prepacked lengths?  Tag or size bits?
169