Make der_pack_prepack() static
[quick-der] / lib / der_pack.c
1 #include <quick-der/api.h>
2
3
4 /* Backward-insert the bytes for the individual entries of the given derprep
5  * structure; the entry is assumed to be setup with prepack, and so to contain
6  * a derray pointing to an array of dercursor, and the derlen_msb is supposed
7  * to be the element count with the highest bit set for the
8  * DER_DERLEN_FLAG_CONSTRUCTED flagging.
9  *
10  * This function does not return failure under the assumption that a
11  * properly-sized buffer is available for it.  The return value is
12  * still the size, because it is needed when processing the data.
13  *
14  * If bufend is NULL, this function can be used to measure the size of the
15  * total insertion.  In this case, the function may return DER_DERLEN_ERROR
16  * to indicate an error.
17  */
18 static size_t der_pack_prepack (const derprep *derp, uint8_t **bufend) {
19         size_t totlen = 0;
20         size_t elmlen;
21         size_t cnt = derp->derlen_msb & ~DER_DERLEN_FLAG_CONSTRUCTED;
22         dercursor *crs = derp->derray;
23         uint8_t *buf;
24         while (cnt-- > 0) {
25                 if (crs->derlen & DER_DERLEN_FLAG_CONSTRUCTED) {
26                         elmlen = der_pack_prepack ((const derprep *) crs+cnt, bufend);
27                         if (elmlen == DER_DERLEN_ERROR) {
28                                 return DER_DERLEN_ERROR;
29                         }
30                 } else {
31                         if (bufend) {
32                                 buf = *bufend;
33                                 buf -= elmlen;
34                                 memcpy (buf, crs [cnt].derptr, elmlen);
35 DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", elmlen, buf);
36                                 *bufend = buf;
37                         }
38                         elmlen = crs->derlen;
39                 }
40                 totlen += elmlen;
41                 if ((totlen | elmlen) & DER_DERLEN_FLAG_CONSTRUCTED) {
42                         return DER_DERLEN_ERROR;
43                 }
44         }
45         return totlen;
46 }
47
48
49 /* Backward-insert the bytes for der_pack() for the given syntax, using the
50  * DER array for elementary values.  Special handling is provided when a
51  * BIT STRING is entered; this encapsulates byte-aligned DER codes into a
52  * bit-aligned BIT STRING, so we can insert the remainder bits set to 0.
53  * Also handled specially is DER_PACK_ANY, which causes the entire structure
54  * to be stored or returned, including its DER header.
55  *
56  * The routine returns 0 if it encounters an error, or otherwise the number
57  * of bytes filled.  When it is called with a non-NULL bufend and an output
58  * buffer of the right size, it will not return an error.
59  */
60 static size_t der_pack_rec (const derwalk *syntax, int *stxlen,
61                                 uint8_t **bufend,
62                                 const dercursor *derray, size_t *offsetp) {
63         size_t totlen = 0;
64         size_t elmlen = 0;
65         size_t tmplen;
66         bool addhdr;
67         bool bitstr;
68         uint8_t cmd;
69         uint8_t tag;
70         uint8_t *buf;
71         uint8_t lenlen;
72         const dercursor *dernext;
73 DPRINTF ("DEBUG: Entered recursive call der_pack_rec() with bufend=0x%016llx\n", bufend? *bufend: 0);
74         do {
75                 // deref stxend; decrease the stored pointer; deref that pointer:
76                 tag = cmd = syntax [-- *stxlen];
77                 bitstr = (cmd == (DER_PACK_ENTER | DER_TAG_BITSTRING));;
78 DPRINTF ("DEBUG: Command to pack_rec() is 0x%02x, collected length is %zd, offset is %zd\n", cmd, totlen, *offsetp);
79                 // Note: DER_PACK_ANY ends up under DER_PACK_STORE below
80                 if ((cmd == DER_PACK_CHOICE_BEGIN)
81                                         || (cmd == DER_PACK_CHOICE_END)
82                                         || (cmd == DER_PACK_OPTIONAL)) {
83                         // Skip, and rely on consistent NULL dercursor entries
84 DPRINTF ("DEBUG: Choice|Optional command has no data\n");
85                         cmd = 0x00;  // Avoid falling out (OPTIONAL & ENTER)
86                         continue;
87                 } else if (cmd & DER_PACK_ENTER) {
88                         // Ends current (recursive) der_pack_rec() for sub-part
89                         // Continue below, where the <tag,elmlen> header is added
90                         addhdr = 1;
91                         elmlen = totlen;
92                         totlen = bitstr? 1: 0;
93 DPRINTF ("DEBUG: Post-enter element, moved totlen %zd to element length\n", elmlen);
94                 } else if (cmd == DER_PACK_LEAVE) {
95                         // Make a recursive call for what precedes DER_PACK_LEAVE
96                         elmlen = der_pack_rec (syntax, stxlen, bufend, derray, offsetp);
97                         if (elmlen == DER_DERLEN_ERROR) {
98                                 return DER_DERLEN_ERROR;
99                         }
100                         addhdr = 0;
101 DPRINTF ("DEBUG: Recursive element length set to %zd\n", elmlen);
102                 } else {
103                         // We have hit upon a DER_PACK_STORE value (includes ANY)
104                         addhdr = (cmd != DER_PACK_ANY);
105                         // Consume one array element, even if it will be NULL
106                         (*offsetp)--;
107                         dernext = derray + *offsetp;    // offset may have changed
108 DPRINTF ("DEBUG: Updated offset to %zd, pointer is 0x%016llx\n", *offsetp, dernext);
109                         if (der_isnull (dernext)) {
110                                 // Do not pack this entry, DEFAULT or CHOICE
111                                 elmlen = 0;
112                                 addhdr = 0;
113 DPRINTF ("DEBUG: Consumed NULL entry at %zd, so elmlen set to 0\n", *offsetp);
114                         } else if (derray->derlen & DER_DERLEN_FLAG_CONSTRUCTED) {
115                                 // Prepacked Constructed, non-NULL entry
116                                 elmlen = der_pack_prepack ((const derprep *) dernext, bufend);
117                                 if (elmlen == DER_DERLEN_ERROR) {
118                                         return DER_DERLEN_ERROR;
119                                 }
120 DPRINTF ("DEBUG: Fetched length from constructed, recursive element\n");
121                         } else {
122                                 // Primitive, non-NULL entry
123                                 elmlen = dernext->derlen;
124                                 if ((elmlen > 0) && (bufend != NULL)) {
125                                         buf = *bufend;
126                                         buf -= elmlen;
127                                         memcpy (buf, dernext->derptr, elmlen);
128 DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", elmlen, buf);
129                                         *bufend = buf;
130                                 }
131                                 if ((tag == 0x08) || (tag == 0x0b)
132                                                 || (tag == 0x10) || (tag == 0x11)) {
133                                         tag |= 0x20;    // Constructed, even STORED
134                                 }
135 DPRINTF ("DEBUG: Fetched length from primitive element\n");
136                         }
137 DPRINTF ("DEBUG: Stored element length set to %zd at offset %zd\n", elmlen, *offsetp);
138                 }
139                 if (addhdr) {
140                         if (bufend) {
141                                 buf = *bufend;
142                                 if (bitstr) {
143                                         * --buf = 0x00;
144                                 }
145                         }
146                         lenlen = 0;
147                         if (elmlen >= 0x80) {
148                                 tmplen = elmlen;
149                                 while (tmplen > 0) {
150                                         if (bufend) {
151                                                 * -- buf = (tmplen & 0xff);
152                                         }
153                                         tmplen >>= 8;
154                                         lenlen++;
155                                 }
156                         }
157                         if (bufend) {
158                                 * -- buf = (elmlen >= 0x80)? (lenlen|0x80): elmlen;
159                                 * -- buf = tag;
160                                 * bufend = buf;
161 DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", 2 + lenlen, buf);
162                         }
163                         elmlen += 2 + lenlen;
164                 }
165 DPRINTF ("DEBUG: Adding %zd to length %zd, collected length is %zd\n", elmlen, totlen, elmlen + totlen);
166                 totlen += elmlen;
167                 if ((elmlen | totlen) & DER_DERLEN_FLAG_CONSTRUCTED) {
168                         return DER_DERLEN_ERROR;
169                 }
170         // Special cases: DER_PACK_OPTIONAL has had the cmd value reset to 0x00
171         // Note: The while loop terminates *after* processing the ENTER cmd
172         } while (((cmd & DER_PACK_ENTER) == 0x00) && (*stxlen > 0));
173 DPRINTF ("DEBUG: Leaving recursive call der_pack_rec() with bufend=0x%016llx\n", bufend? *bufend: 0);
174         return totlen;
175 }
176
177
178 /* Pack a memory buffer following the indicated syntax, and using the elements
179  * stored in the derray.  Enough memory is assumed to be available _before_
180  * outbuf_end_opt; to find how large this buffer needs to be, it is possible to
181  * call this function with outbuf_end_opt set to NULL.
182  *
183  * The return value is the same, regardless of outbuf_end_opt being NULL or not;
184  * it is the length of the required output buffer.  When an error occurs, the
185  * value 0 is returned, but that cannot happen on a second run on the same data
186  * with only the outbuf_end_opt set to non-NULL.
187  *
188  * Please note once more that outbuf_end_opt, when non-NULL, points to the
189  * first byte that is _not_ filled with the output DER data.  The value will
190  * be decremented in this function for the bytes written.  This is quite
191  * simply a more optimal strategy for DER production than anything else.
192  * And yes, this is funny in an API, but you have the information and we would
193  * otherwise ask you to pass it in, need to check it, you would then need to
194  * check for extra error returns, ... so this is in fact simpler.
195  *
196  * Any parts of this structure that need to be prepacked are assumed to have
197  * been prepared with der_prepack().  If your packaged structures show up as
198  * Primitive where they should have been Constructed, then this is where to
199  * look.
200  */
201 size_t der_pack (const derwalk *syntax, const dercursor *derray,
202                                         uint8_t *outbuf_end_opt) {
203         int entered = 0;
204         uint8_t cmd;
205         int stxlen = 0;
206         size_t derraylen = 0;
207         size_t totlen = 0;
208         while (cmd = syntax [stxlen], (entered > 0) || (cmd != DER_PACK_END)) {
209                 stxlen++;
210                 if (cmd & DER_PACK_ENTER) {
211                         if (cmd != DER_PACK_OPTIONAL) {
212                                 entered++;
213                         }
214                 } else {
215                         if (cmd == DER_PACK_LEAVE) {
216                                 entered--;
217                         } else if ((cmd != DER_PACK_CHOICE_BEGIN) && (cmd != DER_PACK_CHOICE_END)) {
218                                 // Remaining commands store data (including ANY)
219                                 derraylen++;
220                         }
221                 }
222         }
223 DPRINTF ("DEBUG: Skipping %d syntax bytes, ending in %02x %02x %02x %02x | %02x\n", stxlen, syntax [-4], syntax [-3], syntax [-2], syntax [-1], syntax [0]);
224         while (stxlen > 0) {
225                 totlen += der_pack_rec (syntax, &stxlen,
226                                 outbuf_end_opt? &outbuf_end_opt: NULL,
227                                 derray, &derraylen);
228         }
229         // One could assert() on derraylen == NULL, and syntax back to initial
230         return totlen;
231 }