File Coverage

src/pdfmake_asn1.c
Criterion Covered Total %
statement 157 445 35.2
branch 93 346 26.8
condition n/a
subroutine n/a
pod n/a
total 250 791 31.6


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_asn1.c — ASN.1 DER encoding/decoding implementation
3             *
4             * Minimal ASN.1 DER codec for X.509, PKCS#7, PKCS#12 parsing and building.
5             */
6              
7             #include "pdfmake_asn1.h"
8             #include "pdfmake_arena.h"
9             #include "pdfmake_buf.h"
10             #include
11             #include
12             #include
13             #include
14              
15             /*============================================================================
16             * Internal helpers
17             *==========================================================================*/
18              
19             /* Parse DER length field */
20 143           static int parse_length(const uint8_t *data, size_t len, size_t *pos, size_t *out_len)
21             {
22             uint8_t first;
23             size_t num_bytes;
24             size_t length;
25             size_t i;
26              
27 143 50         if (*pos >= len) return -1;
28            
29 143           first = data[(*pos)++];
30            
31 143 100         if (first < 0x80) {
32             /* Short form: length in single byte */
33 102           *out_len = first;
34 102           return 0;
35             }
36            
37 41 50         if (first == 0x80) {
38             /* Indefinite length - not valid in DER */
39 0           return -1;
40             }
41            
42             /* Long form: first byte indicates number of length bytes */
43 41           num_bytes = first & 0x7F;
44 41 50         if (num_bytes > sizeof(size_t) || *pos + num_bytes > len) {
    50          
45 0           return -1;
46             }
47            
48 41           length = 0;
49 118 100         for (i = 0; i < num_bytes; i++) {
50 77           length = (length << 8) | data[(*pos)++];
51             }
52            
53 41           *out_len = length;
54 41           return 0;
55             }
56              
57             /* Encode DER length field */
58 0           static pdfmake_err_t write_length(pdfmake_buf_t *buf, size_t length)
59             {
60             size_t temp;
61             int num_bytes;
62             pdfmake_err_t err;
63             int i;
64              
65 0 0         if (length < 0x80) {
66 0           return pdfmake_buf_append_byte(buf, (uint8_t)length);
67             }
68            
69             /* Count bytes needed */
70 0           temp = length;
71 0           num_bytes = 0;
72 0 0         while (temp > 0) {
73 0           num_bytes++;
74 0           temp >>= 8;
75             }
76            
77             /* Write length-of-length byte */
78 0           err = pdfmake_buf_append_byte(buf, 0x80 | num_bytes);
79 0 0         if (err != PDFMAKE_OK) return err;
80            
81             /* Write length bytes (big-endian) */
82 0 0         for (i = num_bytes - 1; i >= 0; i--) {
83 0           err = pdfmake_buf_append_byte(buf, (length >> (i * 8)) & 0xFF);
84 0 0         if (err != PDFMAKE_OK) return err;
85             }
86            
87 0           return PDFMAKE_OK;
88             }
89              
90             /*============================================================================
91             * Parsing API
92             *==========================================================================*/
93              
94 143           pdfmake_asn1_node_t *pdfmake_asn1_parse_element(
95             pdfmake_arena_t *arena,
96             const uint8_t *data,
97             size_t len,
98             size_t *pos)
99             {
100             pdfmake_asn1_node_t *node;
101              
102 143 50         if (*pos >= len) return NULL;
103            
104 143           node = pdfmake_arena_alloc(arena, sizeof(pdfmake_asn1_node_t));
105 143 50         if (!node) return NULL;
106            
107 143           memset(node, 0, sizeof(pdfmake_asn1_node_t));
108            
109             /* Parse tag */
110 143           node->tag = data[(*pos)++];
111            
112             /* Handle multi-byte tags (tag number >= 31) */
113 143 50         if ((node->tag & 0x1F) == 0x1F) {
114             /* Multi-byte tag - skip additional tag bytes for now */
115 0 0         while (*pos < len && (data[*pos] & 0x80)) {
    0          
116 0           (*pos)++;
117             }
118 0 0         if (*pos < len) (*pos)++; /* Skip final tag byte */
119             }
120            
121             /* Parse length */
122 143 50         if (parse_length(data, len, pos, &node->length) != 0) {
123 0           return NULL;
124             }
125            
126             /* Check bounds */
127 143 50         if (*pos + node->length > len) {
128 0           return NULL;
129             }
130            
131             /* Set data pointer */
132 143           node->data = data + *pos;
133            
134             /* Parse children for constructed types */
135 143 100         if (node->tag & ASN1_CONSTRUCTED) {
136 65           size_t content_end = *pos + node->length;
137 65           pdfmake_asn1_node_t *last_child = NULL;
138            
139 197 100         while (*pos < content_end) {
140 132           pdfmake_asn1_node_t *child = pdfmake_asn1_parse_element(arena, data, content_end, pos);
141 132 50         if (!child) return NULL;
142            
143 132           child->parent = node;
144            
145 132 100         if (last_child) {
146 67           last_child->next = child;
147             } else {
148 65           node->children = child;
149             }
150 132           last_child = child;
151             }
152             } else {
153             /* Primitive type - skip content */
154 78           *pos += node->length;
155             }
156            
157 143           return node;
158             }
159              
160 3           pdfmake_asn1_node_t *pdfmake_asn1_parse(
161             pdfmake_arena_t *arena,
162             const uint8_t *data,
163             size_t len)
164             {
165 3           size_t pos = 0;
166 3           return pdfmake_asn1_parse_element(arena, data, len, &pos);
167             }
168              
169 0           size_t pdfmake_asn1_child_count(const pdfmake_asn1_node_t *node)
170             {
171 0           size_t count = 0;
172             pdfmake_asn1_node_t *child;
173              
174 0 0         if (!node) return 0;
175            
176 0           child = node->children;
177 0 0         while (child) {
178 0           count++;
179 0           child = child->next;
180             }
181 0           return count;
182             }
183              
184 64           pdfmake_asn1_node_t *pdfmake_asn1_child_at(
185             const pdfmake_asn1_node_t *node,
186             size_t index)
187             {
188             pdfmake_asn1_node_t *child;
189             size_t i;
190              
191 64 50         if (!node) return NULL;
192            
193 64           child = node->children;
194 110 100         for (i = 0; i < index && child; i++) {
    50          
195 46           child = child->next;
196             }
197 64           return child;
198             }
199              
200 2           pdfmake_asn1_node_t *pdfmake_asn1_find_tag(
201             const pdfmake_asn1_node_t *node,
202             uint8_t tag)
203             {
204             pdfmake_asn1_node_t *child;
205              
206 2 50         if (!node) return NULL;
207            
208 2           child = node->children;
209 3 100         while (child) {
210 2 100         if (child->tag == tag) return child;
211 1           child = child->next;
212             }
213 1           return NULL;
214             }
215              
216             /*============================================================================
217             * Value Extraction
218             *==========================================================================*/
219              
220 4           int pdfmake_asn1_get_int64(const pdfmake_asn1_node_t *node, int64_t *out)
221             {
222             int negative;
223             int64_t value;
224             size_t i;
225              
226 4 50         if (!node || !out) return -1;
    50          
227 4 50         if (node->tag != ASN1_TAG_INTEGER && node->tag != ASN1_TAG_ENUMERATED) return -1;
    0          
228 4 50         if (node->length == 0 || node->length > 8) return -1;
    50          
229            
230             /* Check if negative (high bit set) */
231 4           negative = (node->data[0] & 0x80) != 0;
232            
233 4 50         value = negative ? -1 : 0; /* Sign extend */
234 10 100         for (i = 0; i < node->length; i++) {
235 6           value = (value << 8) | node->data[i];
236             }
237            
238 4           *out = value;
239 4           return 0;
240             }
241              
242 0           int pdfmake_asn1_get_uint64(const pdfmake_asn1_node_t *node, uint64_t *out)
243             {
244             const uint8_t *data;
245             size_t len;
246             uint64_t value;
247             size_t i;
248              
249 0 0         if (!node || !out) return -1;
    0          
250 0 0         if (node->tag != ASN1_TAG_INTEGER) return -1;
251 0 0         if (node->length == 0) return -1;
252            
253             /* Skip leading zero byte if present (for positive numbers with high bit set) */
254 0           data = node->data;
255 0           len = node->length;
256            
257 0 0         if (len > 1 && data[0] == 0x00) {
    0          
258 0           data++;
259 0           len--;
260             }
261            
262 0 0         if (len > 8) return -1; /* Too large */
263            
264 0           value = 0;
265 0 0         for (i = 0; i < len; i++) {
266 0           value = (value << 8) | data[i];
267             }
268            
269 0           *out = value;
270 0           return 0;
271             }
272              
273 1           int pdfmake_asn1_get_bool(const pdfmake_asn1_node_t *node, int *out)
274             {
275 1 50         if (!node || !out) return -1;
    50          
276 1 50         if (node->tag != ASN1_TAG_BOOLEAN) return -1;
277 1 50         if (node->length != 1) return -1;
278            
279 1           *out = (node->data[0] != 0);
280 1           return 0;
281             }
282              
283 15           char *pdfmake_asn1_get_oid_string(
284             pdfmake_arena_t *arena,
285             const pdfmake_asn1_node_t *node)
286             {
287             char buf[256];
288             char *p;
289             char *end;
290             int first;
291             int second;
292             size_t i;
293             size_t len;
294             char *result;
295              
296 15 50         if (!node || !arena) return NULL;
    50          
297 15 50         if (node->tag != ASN1_TAG_OID) return NULL;
298 15 50         if (node->length == 0) return NULL;
299            
300             /* Build string representation */
301 15           p = buf;
302 15           end = buf + sizeof(buf) - 1;
303            
304             /* First byte encodes first two components: first * 40 + second */
305 15           first = node->data[0] / 40;
306 15           second = node->data[0] % 40;
307            
308             /* Special case for first component >= 2 */
309 15 100         if (first >= 2) {
310 6           first = 2;
311 6           second = node->data[0] - 80;
312             }
313            
314 15           p += snprintf(p, end - p, "%d.%d", first, second);
315            
316             /* Remaining components are base-128 encoded */
317 15           i = 1;
318 78 100         while (i < node->length && p < end) {
    50          
319 63           uint32_t value = 0;
320 90 50         while (i < node->length) {
321 90           uint8_t byte = node->data[i++];
322 90           value = (value << 7) | (byte & 0x7F);
323 90 100         if (!(byte & 0x80)) break;
324             }
325 63           p += snprintf(p, end - p, ".%u", value);
326             }
327            
328 15           len = p - buf;
329 15           result = pdfmake_arena_alloc(arena, len + 1);
330 15 50         if (result) {
331 15           memcpy(result, buf, len + 1);
332             }
333 15           return result;
334             }
335              
336 12           int pdfmake_asn1_oid_equals(
337             const pdfmake_asn1_node_t *node,
338             const char *oid_str)
339             {
340             uint8_t encoded[64];
341 12           size_t encoded_len = 0;
342             const char *p;
343 12           int first = -1, second = -1;
344              
345 12 50         if (!node || !oid_str) return 0;
    50          
346 12 50         if (node->tag != ASN1_TAG_OID) return 0;
347            
348             /* Encode expected OID and compare bytes */
349 12           p = oid_str;
350            
351 67 100         while (*p && encoded_len < sizeof(encoded)) {
    50          
352             /* Parse next component */
353 55           int value = 0;
354 145 100         while (*p >= '0' && *p <= '9') {
    50          
355 90           value = value * 10 + (*p - '0');
356 90           p++;
357             }
358 55 100         if (*p == '.') p++;
359            
360 55 100         if (first < 0) {
361 12           first = value;
362 43 100         } else if (second < 0) {
363 12           second = value;
364             /* Encode first two components */
365 12           encoded[encoded_len++] = first * 40 + second;
366             } else {
367             /* Encode as base-128 */
368             uint8_t temp[5];
369 31           int temp_len = 0;
370             int i;
371             do {
372 37           temp[temp_len++] = value & 0x7F;
373 37           value >>= 7;
374 37 100         } while (value > 0);
375            
376 68 100         for (i = temp_len - 1; i >= 0; i--) {
377 37 100         encoded[encoded_len++] = temp[i] | (i > 0 ? 0x80 : 0);
378             }
379             }
380             }
381            
382 12 50         if (encoded_len != node->length) return 0;
383 12           return memcmp(encoded, node->data, encoded_len) == 0;
384             }
385              
386 6           char *pdfmake_asn1_get_string(
387             pdfmake_arena_t *arena,
388             const pdfmake_asn1_node_t *node)
389             {
390             char *result;
391              
392 6 50         if (!node || !arena) return NULL;
    50          
393            
394             /* Accept various string types */
395 6 50         switch (node->tag) {
396 6           case ASN1_TAG_UTF8STRING:
397             case ASN1_TAG_PRINTABLESTRING:
398             case ASN1_TAG_IA5STRING:
399             case ASN1_TAG_T61STRING:
400             case ASN1_TAG_VISIBLESTRING:
401             case ASN1_TAG_NUMERICSTRING:
402             case ASN1_TAG_BMPSTRING:
403             case ASN1_TAG_UNIVERSALSTRING:
404 6           break;
405 0           default:
406 0           return NULL;
407             }
408            
409             /* For BMPString, convert from UCS-2 to UTF-8 */
410 6 50         if (node->tag == ASN1_TAG_BMPSTRING) {
411             /* Simple conversion assuming ASCII subset */
412 0           size_t out_len = node->length / 2;
413             size_t i;
414 0           result = pdfmake_arena_alloc(arena, out_len + 1);
415 0 0         if (result) {
416 0 0         for (i = 0; i < out_len; i++) {
417 0           uint16_t ch = (node->data[i*2] << 8) | node->data[i*2 + 1];
418 0 0         result[i] = (ch < 128) ? ch : '?';
419             }
420 0           result[out_len] = '\0';
421             }
422 0           return result;
423             }
424            
425             /* Copy as-is for other string types */
426 6           result = pdfmake_arena_alloc(arena, node->length + 1);
427 6 50         if (result) {
428 6           memcpy(result, node->data, node->length);
429 6           result[node->length] = '\0';
430             }
431 6           return result;
432             }
433              
434 2           int pdfmake_asn1_get_time(const pdfmake_asn1_node_t *node, int64_t *out)
435             {
436             struct tm tm;
437             const char *s;
438             size_t len;
439              
440 2 50         if (!node || !out) return -1;
    50          
441 2 50         if (node->tag != ASN1_TAG_UTCTIME && node->tag != ASN1_TAG_GENERALIZEDTIME) {
    0          
442 0           return -1;
443             }
444            
445 2           memset(&tm, 0, sizeof(tm));
446 2           s = (const char *)node->data;
447 2           len = node->length;
448            
449 2 50         if (node->tag == ASN1_TAG_UTCTIME) {
450             /* YYMMDDhhmmssZ or YYMMDDhhmmss+hhmm */
451 2 50         if (len < 12) return -1;
452            
453 2           tm.tm_year = (s[0] - '0') * 10 + (s[1] - '0');
454 2 50         if (tm.tm_year < 50) tm.tm_year += 100; /* 2000-2049 */
455 2           tm.tm_mon = (s[2] - '0') * 10 + (s[3] - '0') - 1;
456 2           tm.tm_mday = (s[4] - '0') * 10 + (s[5] - '0');
457 2           tm.tm_hour = (s[6] - '0') * 10 + (s[7] - '0');
458 2           tm.tm_min = (s[8] - '0') * 10 + (s[9] - '0');
459 2           tm.tm_sec = (s[10] - '0') * 10 + (s[11] - '0');
460             } else {
461             /* GeneralizedTime: YYYYMMDDhhmmssZ */
462 0 0         if (len < 14) return -1;
463            
464 0           tm.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 +
465 0           (s[2] - '0') * 10 + (s[3] - '0') - 1900;
466 0           tm.tm_mon = (s[4] - '0') * 10 + (s[5] - '0') - 1;
467 0           tm.tm_mday = (s[6] - '0') * 10 + (s[7] - '0');
468 0           tm.tm_hour = (s[8] - '0') * 10 + (s[9] - '0');
469 0           tm.tm_min = (s[10] - '0') * 10 + (s[11] - '0');
470 0           tm.tm_sec = (s[12] - '0') * 10 + (s[13] - '0');
471             }
472            
473             /* Convert to Unix timestamp (assume UTC) */
474 2           tm.tm_isdst = 0;
475            
476             #ifdef _WIN32
477             *out = _mkgmtime(&tm);
478             #else
479 2           *out = timegm(&tm);
480             #endif
481            
482 2           return 0;
483             }
484              
485 2           int pdfmake_asn1_get_bit_string(
486             const pdfmake_asn1_node_t *node,
487             const uint8_t **bits,
488             size_t *bit_count)
489             {
490             uint8_t unused;
491              
492 2 50         if (!node || !bits || !bit_count) return -1;
    50          
    50          
493 2 50         if (node->tag != ASN1_TAG_BIT_STRING) return -1;
494 2 50         if (node->length < 1) return -1;
495            
496             /* First byte is number of unused bits in last byte */
497 2           unused = node->data[0];
498 2 50         if (unused > 7) return -1;
499            
500 2           *bits = node->data + 1;
501 2           *bit_count = (node->length - 1) * 8 - unused;
502            
503 2           return 0;
504             }
505              
506             /*============================================================================
507             * Encoding API
508             *==========================================================================*/
509              
510 0           pdfmake_err_t pdfmake_asn1_encoder_init(
511             pdfmake_asn1_encoder_t *enc,
512             pdfmake_arena_t *arena,
513             pdfmake_buf_t *buf)
514             {
515 0 0         if (!enc || !arena || !buf) return PDFMAKE_EINVAL;
    0          
    0          
516            
517 0           enc->arena = arena;
518 0           enc->buf = buf;
519 0           return PDFMAKE_OK;
520             }
521              
522 0           pdfmake_err_t pdfmake_asn1_write_header(
523             pdfmake_asn1_encoder_t *enc,
524             uint8_t tag,
525             size_t length)
526             {
527 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, tag);
528 0 0         if (err != PDFMAKE_OK) return err;
529 0           return write_length(enc->buf, length);
530             }
531              
532 0           pdfmake_err_t pdfmake_asn1_write_null(pdfmake_asn1_encoder_t *enc)
533             {
534 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_NULL);
535 0 0         if (err != PDFMAKE_OK) return err;
536 0           return pdfmake_buf_append_byte(enc->buf, 0x00);
537             }
538              
539 0           pdfmake_err_t pdfmake_asn1_write_bool(pdfmake_asn1_encoder_t *enc, int value)
540             {
541 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_BOOLEAN);
542 0 0         if (err != PDFMAKE_OK) return err;
543 0           err = pdfmake_buf_append_byte(enc->buf, 0x01);
544 0 0         if (err != PDFMAKE_OK) return err;
545 0 0         return pdfmake_buf_append_byte(enc->buf, value ? 0xFF : 0x00);
546             }
547              
548 0           pdfmake_err_t pdfmake_asn1_write_int64(pdfmake_asn1_encoder_t *enc, int64_t value)
549             {
550             uint8_t bytes[9];
551 0           int len = 0;
552             pdfmake_err_t err;
553            
554 0 0         if (value == 0) {
555 0           bytes[0] = 0;
556 0           len = 1;
557 0 0         } else if (value > 0) {
558             /* Positive: encode as minimal big-endian, add leading 0 if high bit set */
559 0           uint64_t v = value;
560 0 0         while (v > 0) {
561 0           bytes[8 - len++] = v & 0xFF;
562 0           v >>= 8;
563             }
564 0           memmove(bytes, bytes + 9 - len, len);
565            
566 0 0         if (bytes[0] & 0x80) {
567 0           memmove(bytes + 1, bytes, len);
568 0           bytes[0] = 0;
569 0           len++;
570             }
571             } else {
572             /* Negative: encode as two's complement */
573 0           uint64_t v = (uint64_t)value;
574 0           int all_ff = 1;
575             int i;
576 0 0         for (i = 0; i < 8; i++) {
577 0           uint8_t b = (v >> (56 - i * 8)) & 0xFF;
578 0 0         if (all_ff && b == 0xFF && i < 7) {
    0          
    0          
579 0           uint8_t next = (v >> (48 - i * 8)) & 0xFF;
580 0 0         if (next & 0x80) continue; /* Can skip this 0xFF */
581             }
582 0           all_ff = 0;
583 0           bytes[len++] = b;
584             }
585             }
586            
587 0           err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_INTEGER);
588 0 0         if (err != PDFMAKE_OK) return err;
589 0           err = write_length(enc->buf, len);
590 0 0         if (err != PDFMAKE_OK) return err;
591 0           return pdfmake_buf_append(enc->buf, bytes, len);
592             }
593              
594 0           pdfmake_err_t pdfmake_asn1_write_uint64(pdfmake_asn1_encoder_t *enc, uint64_t value)
595             {
596             uint8_t bytes[9];
597 0           int len = 0;
598             pdfmake_err_t err;
599            
600 0 0         if (value == 0) {
601 0           bytes[0] = 0;
602 0           len = 1;
603             } else {
604             /* Encode as minimal big-endian */
605 0           uint64_t v = value;
606 0 0         while (v > 0) {
607 0           bytes[8 - len++] = v & 0xFF;
608 0           v >>= 8;
609             }
610 0           memmove(bytes, bytes + 9 - len, len);
611            
612             /* Add leading 0 if high bit set (to indicate positive) */
613 0 0         if (bytes[0] & 0x80) {
614 0           memmove(bytes + 1, bytes, len);
615 0           bytes[0] = 0;
616 0           len++;
617             }
618             }
619            
620 0           err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_INTEGER);
621 0 0         if (err != PDFMAKE_OK) return err;
622 0           err = write_length(enc->buf, len);
623 0 0         if (err != PDFMAKE_OK) return err;
624 0           return pdfmake_buf_append(enc->buf, bytes, len);
625             }
626              
627 0           pdfmake_err_t pdfmake_asn1_write_integer(
628             pdfmake_asn1_encoder_t *enc,
629             const uint8_t *bytes,
630             size_t len)
631             {
632             int need_zero;
633             pdfmake_err_t err;
634              
635 0 0         if (!enc || !bytes) return PDFMAKE_EINVAL;
    0          
636            
637             /* Skip leading zeros but keep at least one byte */
638 0 0         while (len > 1 && bytes[0] == 0 && !(bytes[1] & 0x80)) {
    0          
    0          
639 0           bytes++;
640 0           len--;
641             }
642            
643             /* Add leading zero if high bit set (to indicate positive) */
644 0           need_zero = (bytes[0] & 0x80) != 0;
645            
646 0           err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_INTEGER);
647 0 0         if (err != PDFMAKE_OK) return err;
648 0 0         err = write_length(enc->buf, len + (need_zero ? 1 : 0));
649 0 0         if (err != PDFMAKE_OK) return err;
650            
651 0 0         if (need_zero) {
652 0           err = pdfmake_buf_append_byte(enc->buf, 0x00);
653 0 0         if (err != PDFMAKE_OK) return err;
654             }
655            
656 0           return pdfmake_buf_append(enc->buf, bytes, len);
657             }
658              
659 0           pdfmake_err_t pdfmake_asn1_write_oid(
660             pdfmake_asn1_encoder_t *enc,
661             const char *oid_str)
662             {
663             uint8_t encoded[64];
664 0           size_t encoded_len = 0;
665             const char *p;
666 0           int first = -1, second = -1;
667             pdfmake_err_t err;
668              
669 0 0         if (!enc || !oid_str) return PDFMAKE_EINVAL;
    0          
670            
671 0           p = oid_str;
672            
673 0 0         while (*p && encoded_len < sizeof(encoded)) {
    0          
674 0           int value = 0;
675 0 0         while (*p >= '0' && *p <= '9') {
    0          
676 0           value = value * 10 + (*p - '0');
677 0           p++;
678             }
679 0 0         if (*p == '.') p++;
680            
681 0 0         if (first < 0) {
682 0           first = value;
683 0 0         } else if (second < 0) {
684 0           second = value;
685 0           encoded[encoded_len++] = first * 40 + second;
686             } else {
687             /* Encode as base-128 */
688             uint8_t temp[5];
689 0           int temp_len = 0;
690             int i;
691             do {
692 0           temp[temp_len++] = value & 0x7F;
693 0           value >>= 7;
694 0 0         } while (value > 0);
695            
696 0 0         for (i = temp_len - 1; i >= 0; i--) {
697 0 0         if (encoded_len >= sizeof(encoded)) return PDFMAKE_EINVAL;
698 0 0         encoded[encoded_len++] = temp[i] | (i > 0 ? 0x80 : 0);
699             }
700             }
701             }
702            
703 0           err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_OID);
704 0 0         if (err != PDFMAKE_OK) return err;
705 0           err = write_length(enc->buf, encoded_len);
706 0 0         if (err != PDFMAKE_OK) return err;
707 0           return pdfmake_buf_append(enc->buf, encoded, encoded_len);
708             }
709              
710 0           pdfmake_err_t pdfmake_asn1_write_octet_string(
711             pdfmake_asn1_encoder_t *enc,
712             const uint8_t *data,
713             size_t len)
714             {
715 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_OCTET_STRING);
716 0 0         if (err != PDFMAKE_OK) return err;
717 0           err = write_length(enc->buf, len);
718 0 0         if (err != PDFMAKE_OK) return err;
719 0           return pdfmake_buf_append(enc->buf, data, len);
720             }
721              
722 0           pdfmake_err_t pdfmake_asn1_write_bit_string(
723             pdfmake_asn1_encoder_t *enc,
724             const uint8_t *bits,
725             size_t bit_count)
726             {
727 0           size_t byte_count = (bit_count + 7) / 8;
728 0           uint8_t unused = (byte_count * 8 - bit_count) % 8;
729            
730 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_BIT_STRING);
731 0 0         if (err != PDFMAKE_OK) return err;
732 0           err = write_length(enc->buf, byte_count + 1);
733 0 0         if (err != PDFMAKE_OK) return err;
734 0           err = pdfmake_buf_append_byte(enc->buf, unused);
735 0 0         if (err != PDFMAKE_OK) return err;
736 0           return pdfmake_buf_append(enc->buf, bits, byte_count);
737             }
738              
739 0           pdfmake_err_t pdfmake_asn1_write_utf8_string(
740             pdfmake_asn1_encoder_t *enc,
741             const char *str)
742             {
743 0           size_t len = strlen(str);
744 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_UTF8STRING);
745 0 0         if (err != PDFMAKE_OK) return err;
746 0           err = write_length(enc->buf, len);
747 0 0         if (err != PDFMAKE_OK) return err;
748 0           return pdfmake_buf_append(enc->buf, (const uint8_t *)str, len);
749             }
750              
751 0           pdfmake_err_t pdfmake_asn1_write_printable_string(
752             pdfmake_asn1_encoder_t *enc,
753             const char *str)
754             {
755 0           size_t len = strlen(str);
756 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_PRINTABLESTRING);
757 0 0         if (err != PDFMAKE_OK) return err;
758 0           err = write_length(enc->buf, len);
759 0 0         if (err != PDFMAKE_OK) return err;
760 0           return pdfmake_buf_append(enc->buf, (const uint8_t *)str, len);
761             }
762              
763 0           pdfmake_err_t pdfmake_asn1_write_ia5_string(
764             pdfmake_asn1_encoder_t *enc,
765             const char *str)
766             {
767 0           size_t len = strlen(str);
768 0           pdfmake_err_t err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_IA5STRING);
769 0 0         if (err != PDFMAKE_OK) return err;
770 0           err = write_length(enc->buf, len);
771 0 0         if (err != PDFMAKE_OK) return err;
772 0           return pdfmake_buf_append(enc->buf, (const uint8_t *)str, len);
773             }
774              
775 0           pdfmake_err_t pdfmake_asn1_write_utc_time(
776             pdfmake_asn1_encoder_t *enc,
777             int64_t timestamp)
778             {
779 0           time_t t = (time_t)timestamp;
780 0           struct tm *tm = gmtime(&t);
781             char buf[16];
782             int year;
783             pdfmake_err_t err;
784              
785 0 0         if (!tm) return PDFMAKE_EINVAL;
786            
787 0           year = tm->tm_year % 100; /* Two-digit year */
788 0           snprintf(buf, sizeof(buf), "%02d%02d%02d%02d%02d%02dZ",
789 0           year, tm->tm_mon + 1, tm->tm_mday,
790             tm->tm_hour, tm->tm_min, tm->tm_sec);
791            
792 0           err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_UTCTIME);
793 0 0         if (err != PDFMAKE_OK) return err;
794 0           err = write_length(enc->buf, 13);
795 0 0         if (err != PDFMAKE_OK) return err;
796 0           return pdfmake_buf_append(enc->buf, (const uint8_t *)buf, 13);
797             }
798              
799 0           pdfmake_err_t pdfmake_asn1_write_generalized_time(
800             pdfmake_asn1_encoder_t *enc,
801             int64_t timestamp)
802             {
803 0           time_t t = (time_t)timestamp;
804 0           struct tm *tm = gmtime(&t);
805             char buf[20];
806             pdfmake_err_t err;
807              
808 0 0         if (!tm) return PDFMAKE_EINVAL;
809            
810 0           snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02dZ",
811 0           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
812             tm->tm_hour, tm->tm_min, tm->tm_sec);
813            
814 0           err = pdfmake_buf_append_byte(enc->buf, ASN1_TAG_GENERALIZEDTIME);
815 0 0         if (err != PDFMAKE_OK) return err;
816 0           err = write_length(enc->buf, 15);
817 0 0         if (err != PDFMAKE_OK) return err;
818 0           return pdfmake_buf_append(enc->buf, (const uint8_t *)buf, 15);
819             }
820              
821 0           size_t pdfmake_asn1_begin_sequence(pdfmake_asn1_encoder_t *enc)
822             {
823 0           size_t pos = enc->buf->len;
824 0           pdfmake_buf_append_byte(enc->buf, ASN1_TAG_SEQUENCE | ASN1_CONSTRUCTED);
825             /* Reserve 4 bytes for length (will be fixed up later) */
826 0           pdfmake_buf_append_byte(enc->buf, 0x84);
827 0           pdfmake_buf_append_byte(enc->buf, 0x00);
828 0           pdfmake_buf_append_byte(enc->buf, 0x00);
829 0           pdfmake_buf_append_byte(enc->buf, 0x00);
830 0           pdfmake_buf_append_byte(enc->buf, 0x00);
831 0           return pos;
832             }
833              
834 0           size_t pdfmake_asn1_begin_set(pdfmake_asn1_encoder_t *enc)
835             {
836 0           size_t pos = enc->buf->len;
837 0           pdfmake_buf_append_byte(enc->buf, ASN1_TAG_SET | ASN1_CONSTRUCTED);
838             /* Reserve 4 bytes for length */
839 0           pdfmake_buf_append_byte(enc->buf, 0x84);
840 0           pdfmake_buf_append_byte(enc->buf, 0x00);
841 0           pdfmake_buf_append_byte(enc->buf, 0x00);
842 0           pdfmake_buf_append_byte(enc->buf, 0x00);
843 0           pdfmake_buf_append_byte(enc->buf, 0x00);
844 0           return pos;
845             }
846              
847 0           size_t pdfmake_asn1_begin_context(
848             pdfmake_asn1_encoder_t *enc,
849             uint8_t tag_number,
850             int constructed)
851             {
852 0           size_t pos = enc->buf->len;
853 0           uint8_t tag = ASN1_CLASS_CONTEXT | tag_number;
854 0 0         if (constructed) tag |= ASN1_CONSTRUCTED;
855 0           pdfmake_buf_append_byte(enc->buf, tag);
856             /* Reserve 4 bytes for length */
857 0           pdfmake_buf_append_byte(enc->buf, 0x84);
858 0           pdfmake_buf_append_byte(enc->buf, 0x00);
859 0           pdfmake_buf_append_byte(enc->buf, 0x00);
860 0           pdfmake_buf_append_byte(enc->buf, 0x00);
861 0           pdfmake_buf_append_byte(enc->buf, 0x00);
862 0           return pos;
863             }
864              
865 0           pdfmake_err_t pdfmake_asn1_end_constructed(
866             pdfmake_asn1_encoder_t *enc,
867             size_t start_pos)
868             {
869             /* The begin_* helpers reserved 5 bytes for the length prefix
870             * (0x84 + 4 length bytes). For strict DER (required by CMS
871             * verifiers) the prefix must use the shortest form:
872             * length < 128 → 1 byte
873             * length <= 0xFF → 2 bytes (0x81 + 1)
874             * length <= 0xFFFF → 3 bytes (0x82 + 2)
875             * length <= 0xFFFFFF → 4 bytes (0x83 + 3)
876             * otherwise → 5 bytes (0x84 + 4)
877             * After computing the content length we rewrite the header and, if
878             * the new header is shorter, shift content left to close the gap. */
879              
880 0           size_t content_start = start_pos + 6; /* tag + 5 reserved length bytes */
881 0           size_t content_len = enc->buf->len - content_start;
882              
883             /* Determine minimum length encoding. */
884             int header_len_bytes; /* bytes following the tag (len-of-len + len) */
885 0           int reserved = 5;
886             int shift;
887             uint8_t *base;
888             uint8_t *len_start;
889              
890 0 0         if (content_len < 0x80) header_len_bytes = 1;
891 0 0         else if (content_len <= 0xFF) header_len_bytes = 2;
892 0 0         else if (content_len <= 0xFFFF) header_len_bytes = 3;
893 0 0         else if (content_len <= 0xFFFFFF) header_len_bytes = 4;
894 0           else header_len_bytes = 5;
895              
896 0           shift = reserved - header_len_bytes;
897              
898 0           base = enc->buf->data + start_pos;
899 0           len_start = base + 1;
900              
901             /* If the minimum encoding is shorter, slide the content bytes left
902             * by `shift` bytes and decrement the buffer length. */
903 0 0         if (shift > 0 && content_len > 0) {
    0          
904 0           memmove(base + 1 + header_len_bytes,
905 0           base + 1 + reserved,
906             content_len);
907 0           enc->buf->len -= (size_t)shift;
908 0 0         } else if (shift > 0) {
909 0           enc->buf->len -= (size_t)shift;
910             }
911              
912             /* Write the new length header. */
913 0 0         if (header_len_bytes == 1) {
914 0           len_start[0] = (uint8_t)content_len;
915             } else {
916             int i;
917 0           len_start[0] = (uint8_t)(0x80 | (header_len_bytes - 1));
918 0 0         for (i = 1; i < header_len_bytes; i++) {
919 0           int shift_bits = (header_len_bytes - 1 - i) * 8;
920 0           len_start[i] = (uint8_t)((content_len >> shift_bits) & 0xFF);
921             }
922             }
923              
924 0           return PDFMAKE_OK;
925             }
926              
927 0           pdfmake_err_t pdfmake_asn1_write_raw(
928             pdfmake_asn1_encoder_t *enc,
929             const uint8_t *data,
930             size_t len)
931             {
932 0           return pdfmake_buf_append(enc->buf, data, len);
933             }