Logo floats right
[quick-der] / WHEN-SIZE-MATTERS.MD
1 # Quick DER sizes: Sometimes small is bettar
2
3 <img alt="Quick DER logo" src="quick-der-logo.png" style="float: right"/>
4
5 > *This is a decription of the sizes of structures used by Quick DER.  As this
6 > demonstrates, the library is quite useful for embedded purposes.*
7
8 Using Quick DER, an include file is derived from an ASN.1 syntax specification.
9 The include files contain fixed parsers for the various structure that can be
10 assigned to constant byte arrays, and then applied to a dowloaded sequence of
11 bytes in DER format.
12
13 Data is generally stored in the form of a dercursor.  This is a combination
14 of a pointer to DER data with a size of the data from that point.  We measure
15 memory consumption for data storage in such structures; they point the the
16 already-allocated memory that holds to-be-analysed DER input.  The length of
17 the actual data handled is therefore not included below, since it can vary
18 virtually without bounds.
19
20
21 ## Code size
22
23 The following data stems from analysis of the `.text` segment of version TODO
24 of Quick DER:
25
26   * 261 bytes for `der_header()`
27   * 079 bytes for `der_iterate_first()`, `der_iterate_next()` and `der_iterate_next()`
28   * 141 bytes for `der_skip()` and `der_enter()`
29   * 761 bytes for `der_unpack()`
30   * 008 bytes for `der_prepack()`
31   * 788 bytes for `der_pack()`
32   * 376 bytes for `der_walk()`
33
34 Note that `der_header()` is the only dependency for most other modules.
35 Some modules will write into `errno` as well.
36
37 The total size of all these modules is 2405 bytes.  Note that this includes
38 alternatives; it is possible to use just `der_walk()` to walk into structures,
39 or one might do it with `der_unpack()`.  The choice is there but there is no
40 need to mix the styles if you are pressed for space.
41
42
43 ## Data storage
44
45 This is expressed in the number of dercursor structures needed to address
46 portions of already-allocated DER code space.  Note that Quick DER never
47 allocates dynamic memory for itself, so you are free()d from memory management
48 for these routines.
49
50 Moreover, the routines help you to avoid allocating dynamic memory too.  The
51 walker construct, together with basic movements inside DER structures, makes
52 it easy to setup a cursor and have it traverse paths.  And there are iterators
53 to walk through SEQUENCE OF and SET OF structures.
54
55 The only reason you might have to allocate memory is when you intend to fully
56 der_unpack() into SEQUENCE OF and SET OF structures.  In this case, the output
57 in the form of an dercursor array can have any size due to the variable size
58 of the repeated structure.  You may be forced to use this if your aim is to
59 der_pack() the structure later, because it may otherwise end up as a Primitive
60 structure, which may not be what you intended to deliver.  But even then, it
61 is a matter of dercursor structures, not the full-blown packaged data size.
62 After taking ample size precautiouns, this might easily be done on a stack.
63
64 The other components of variability do not require explicit allocation, because
65 the space for all their variants can be unfolded and separately stored.
66 This applies to OPTIONAL / DEFAULT as well as to CHOICE.  Entries that were
67 not parsed are simply set to NULL dercursor values, so it is detectable that
68 they were not present in the input (and should not appear in the output).
69 The ANY element is parsed by providing the entire structure at its position,
70 including its header (and tag).  You can use der_enter() or der_header() to
71 skip or analyse the header, and/or you could start a new der_unpack() or
72 der_walk() to get into the structure, based on the applicable syntax.
73
74 The number of dercursor structures created depends on the parser prescription.
75 Here is the count for der_unpack()ing a few standard structures:
76
77   * 16 dercursor values for a PKIX Certificate
78   * 03 dercursor values for a PKIX Certificate Extension
79   * 07 dercursor values for a Kerberos5 Ticket
80   * 14 dercursor values for a Kerberos5 Ticket's EncTicketPart
81
82 Oh, and the walking paths that instruct the parser are very small too:
83
84   * 46 bytes for a PKIX Certificate
85   * 07 bytes for a PKIX Certificate Extension
86   * 33 bytes for a Kerberos5 Ticket
87   * 55 bytes for a Kerberos5 Ticket's EncTicketPart
88
89 Quite humble, isn't it?
90
91
92 ## Stack size
93
94 The storage needed on the stack depends solely on routine nesting depth.
95 The nesting depth depends on the ASN.1 syntax descriptions being processed;
96 since it does not depend on the data, there is no risk of a stack overflow
97 caused by too large data.  (You should of course still be careful when you
98 allocate dynamically sized, that is, input-determined, data on your stack.)
99
100 With der_walk() there is never any nesting.  For der_pack() and der_unpack()
101 the nesting is determined by the number of ENTER / LEAVE pairs.
102 In addition, der_pack() will nest for components prepared with der_prepack()
103 which might also be prepared in a nested fashion.  In addition, der_unpack()
104 will nest for CHOICE sequences.
105
106 The following indications provide some information on the maximum reached
107 nesting depth while running der_unpack() over some standard structures:
108
109   * 4 nesting levels for a PKIX Certificate
110   * 1 nesting levels for a PKIX Certificate Extension
111   * 5 nesting levels for a Kerberos5 Ticket's unencrypted part
112   * 5 nesting levels for a Kerberos5 Ticket's EncTicketPart
113
114 The reason that Kerberos is using a few more nesting levels is that it uses
115 explicit tagging very liberally.  It's a modest cost for such a bright use
116 of ASN.1 and especially to achieve extensibility in the data formats.
117
118
119 ## Benchmarking Environment
120
121 The aforementioned figures were obtained using:
122
123   * `gcc` 4.7.2
124   * `CFLAGS=-Os -ggdb3 -c`
125   * `objdump -h -j .text *.o`
126   * The first GIT checkin of the source code (may be updated later on)
127
128 The choice for `-Os` was based on the intention to demonstrate suitability
129 for embedded environments.  It saved about 42% code size compared to `-O0`
130 (no optimisation) and about 25% compared to `-O2` (often used in projects as
131 their default optimisation).
132