1 #include <quick-der/api.h>
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.
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.
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.
18 static size_t der_pack_prepack (const derprep *derp, uint8_t **bufend) {
21 size_t cnt = derp->derlen_msb & ~DER_DERLEN_FLAG_CONSTRUCTED;
22 dercursor *crs = derp->derray;
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;
34 memcpy (buf, crs [cnt].derptr, elmlen);
35 DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", elmlen, buf);
41 if ((totlen | elmlen) & DER_DERLEN_FLAG_CONSTRUCTED) {
42 return DER_DERLEN_ERROR;
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.
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.
60 static size_t der_pack_rec (const derwalk *syntax, int *stxlen,
62 const dercursor *derray, size_t *offsetp) {
72 const dercursor *dernext;
73 DPRINTF ("DEBUG: Entered recursive call der_pack_rec() with bufend=0x%016llx\n", bufend? *bufend: 0);
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)
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
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;
101 DPRINTF ("DEBUG: Recursive element length set to %zd\n", elmlen);
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
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
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;
120 DPRINTF ("DEBUG: Fetched length from constructed, recursive element\n");
122 // Primitive, non-NULL entry
123 elmlen = dernext->derlen;
124 if ((elmlen > 0) && (bufend != NULL)) {
127 memcpy (buf, dernext->derptr, elmlen);
128 DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", elmlen, buf);
131 if ((tag == 0x08) || (tag == 0x0b)
132 || (tag == 0x10) || (tag == 0x11)) {
133 tag |= 0x20; // Constructed, even STORED
135 DPRINTF ("DEBUG: Fetched length from primitive element\n");
137 DPRINTF ("DEBUG: Stored element length set to %zd at offset %zd\n", elmlen, *offsetp);
147 if (elmlen >= 0x80) {
151 * -- buf = (tmplen & 0xff);
158 * -- buf = (elmlen >= 0x80)? (lenlen|0x80): elmlen;
161 DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", 2 + lenlen, buf);
163 elmlen += 2 + lenlen;
165 DPRINTF ("DEBUG: Adding %zd to length %zd, collected length is %zd\n", elmlen, totlen, elmlen + totlen);
167 if ((elmlen | totlen) & DER_DERLEN_FLAG_CONSTRUCTED) {
168 return DER_DERLEN_ERROR;
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);
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.
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.
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.
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
201 size_t der_pack (const derwalk *syntax, const dercursor *derray,
202 uint8_t *outbuf_end_opt) {
206 size_t derraylen = 0;
208 while (cmd = syntax [stxlen], (entered > 0) || (cmd != DER_PACK_END)) {
210 if (cmd & DER_PACK_ENTER) {
211 if (cmd != DER_PACK_OPTIONAL) {
215 if (cmd == DER_PACK_LEAVE) {
217 } else if ((cmd != DER_PACK_CHOICE_BEGIN) && (cmd != DER_PACK_CHOICE_END)) {
218 // Remaining commands store data (including ANY)
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]);
225 totlen += der_pack_rec (syntax, &stxlen,
226 outbuf_end_opt? &outbuf_end_opt: NULL,
229 // One could assert() on derraylen == NULL, and syntax back to initial