xref: /third_party/mbedtls/library/asn1write.c (revision a8e1175b)
1/*
2 * ASN.1 buffer writing functionality
3 *
4 *  Copyright The Mbed TLS Contributors
5 *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6 */
7
8#include "common.h"
9
10#if defined(MBEDTLS_ASN1_WRITE_C) || defined(MBEDTLS_X509_USE_C) || \
11    defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
12
13#include "mbedtls/asn1write.h"
14#include "mbedtls/error.h"
15
16#include <string.h>
17
18#include "mbedtls/platform.h"
19
20#if defined(MBEDTLS_ASN1_PARSE_C)
21#include "mbedtls/asn1.h"
22#endif
23
24int mbedtls_asn1_write_len(unsigned char **p, const unsigned char *start, size_t len)
25{
26#if SIZE_MAX > 0xFFFFFFFF
27    if (len > 0xFFFFFFFF) {
28        return MBEDTLS_ERR_ASN1_INVALID_LENGTH;
29    }
30#endif
31
32    int required = 1;
33
34    if (len >= 0x80) {
35        for (size_t l = len; l != 0; l >>= 8) {
36            required++;
37        }
38    }
39
40    if (required > (*p - start)) {
41        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
42    }
43
44    do {
45        *--(*p) = MBEDTLS_BYTE_0(len);
46        len >>= 8;
47    } while (len);
48
49    if (required > 1) {
50        *--(*p) = (unsigned char) (0x80 + required - 1);
51    }
52
53    return required;
54}
55
56int mbedtls_asn1_write_tag(unsigned char **p, const unsigned char *start, unsigned char tag)
57{
58    if (*p - start < 1) {
59        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
60    }
61
62    *--(*p) = tag;
63
64    return 1;
65}
66#endif /* MBEDTLS_ASN1_WRITE_C || MBEDTLS_X509_USE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */
67
68#if defined(MBEDTLS_ASN1_WRITE_C)
69static int mbedtls_asn1_write_len_and_tag(unsigned char **p,
70                                          const unsigned char *start,
71                                          size_t len,
72                                          unsigned char tag)
73{
74    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
75
76    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
77    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, tag));
78
79    return (int) len;
80}
81
82int mbedtls_asn1_write_raw_buffer(unsigned char **p, const unsigned char *start,
83                                  const unsigned char *buf, size_t size)
84{
85    size_t len = 0;
86
87    if (*p < start || (size_t) (*p - start) < size) {
88        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
89    }
90
91    len = size;
92    (*p) -= len;
93    memcpy(*p, buf, len);
94
95    return (int) len;
96}
97
98#if defined(MBEDTLS_BIGNUM_C)
99int mbedtls_asn1_write_mpi(unsigned char **p, const unsigned char *start, const mbedtls_mpi *X)
100{
101    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
102    size_t len = 0;
103
104    // Write the MPI
105    //
106    len = mbedtls_mpi_size(X);
107
108    /* DER represents 0 with a sign bit (0=nonnegative) and 7 value bits, not
109     * as 0 digits. We need to end up with 020100, not with 0200. */
110    if (len == 0) {
111        len = 1;
112    }
113
114    if (*p < start || (size_t) (*p - start) < len) {
115        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
116    }
117
118    (*p) -= len;
119    MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(X, *p, len));
120
121    // DER format assumes 2s complement for numbers, so the leftmost bit
122    // should be 0 for positive numbers and 1 for negative numbers.
123    //
124    if (X->s == 1 && **p & 0x80) {
125        if (*p - start < 1) {
126            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
127        }
128
129        *--(*p) = 0x00;
130        len += 1;
131    }
132
133    ret = mbedtls_asn1_write_len_and_tag(p, start, len, MBEDTLS_ASN1_INTEGER);
134
135cleanup:
136    return ret;
137}
138#endif /* MBEDTLS_BIGNUM_C */
139
140int mbedtls_asn1_write_null(unsigned char **p, const unsigned char *start)
141{
142    // Write NULL
143    //
144    return mbedtls_asn1_write_len_and_tag(p, start, 0, MBEDTLS_ASN1_NULL);
145}
146
147int mbedtls_asn1_write_oid(unsigned char **p, const unsigned char *start,
148                           const char *oid, size_t oid_len)
149{
150    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
151    size_t len = 0;
152
153    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start,
154                                                            (const unsigned char *) oid, oid_len));
155    return mbedtls_asn1_write_len_and_tag(p, start, len, MBEDTLS_ASN1_OID);
156}
157
158int mbedtls_asn1_write_algorithm_identifier(unsigned char **p, const unsigned char *start,
159                                            const char *oid, size_t oid_len,
160                                            size_t par_len)
161{
162    return mbedtls_asn1_write_algorithm_identifier_ext(p, start, oid, oid_len, par_len, 1);
163}
164
165int mbedtls_asn1_write_algorithm_identifier_ext(unsigned char **p, const unsigned char *start,
166                                                const char *oid, size_t oid_len,
167                                                size_t par_len, int has_par)
168{
169    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
170    size_t len = 0;
171
172    if (has_par) {
173        if (par_len == 0) {
174            MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_null(p, start));
175        } else {
176            len += par_len;
177        }
178    }
179
180    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, oid_len));
181
182    return mbedtls_asn1_write_len_and_tag(p, start, len,
183                                          MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
184}
185
186int mbedtls_asn1_write_bool(unsigned char **p, const unsigned char *start, int boolean)
187{
188    size_t len = 0;
189
190    if (*p - start < 1) {
191        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
192    }
193
194    *--(*p) = (boolean) ? 255 : 0;
195    len++;
196
197    return mbedtls_asn1_write_len_and_tag(p, start, len, MBEDTLS_ASN1_BOOLEAN);
198}
199
200static int asn1_write_tagged_int(unsigned char **p, const unsigned char *start, int val, int tag)
201{
202    size_t len = 0;
203
204    do {
205        if (*p - start < 1) {
206            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
207        }
208        len += 1;
209        *--(*p) = val & 0xff;
210        val >>= 8;
211    } while (val > 0);
212
213    if (**p & 0x80) {
214        if (*p - start < 1) {
215            return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
216        }
217        *--(*p) = 0x00;
218        len += 1;
219    }
220
221    return mbedtls_asn1_write_len_and_tag(p, start, len, tag);
222}
223
224int mbedtls_asn1_write_int(unsigned char **p, const unsigned char *start, int val)
225{
226    return asn1_write_tagged_int(p, start, val, MBEDTLS_ASN1_INTEGER);
227}
228
229int mbedtls_asn1_write_enum(unsigned char **p, const unsigned char *start, int val)
230{
231    return asn1_write_tagged_int(p, start, val, MBEDTLS_ASN1_ENUMERATED);
232}
233
234int mbedtls_asn1_write_tagged_string(unsigned char **p, const unsigned char *start, int tag,
235                                     const char *text, size_t text_len)
236{
237    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
238    size_t len = 0;
239
240    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start,
241                                                            (const unsigned char *) text,
242                                                            text_len));
243
244    return mbedtls_asn1_write_len_and_tag(p, start, len, tag);
245}
246
247int mbedtls_asn1_write_utf8_string(unsigned char **p, const unsigned char *start,
248                                   const char *text, size_t text_len)
249{
250    return mbedtls_asn1_write_tagged_string(p, start, MBEDTLS_ASN1_UTF8_STRING, text, text_len);
251}
252
253int mbedtls_asn1_write_printable_string(unsigned char **p, const unsigned char *start,
254                                        const char *text, size_t text_len)
255{
256    return mbedtls_asn1_write_tagged_string(p, start, MBEDTLS_ASN1_PRINTABLE_STRING, text,
257                                            text_len);
258}
259
260int mbedtls_asn1_write_ia5_string(unsigned char **p, const unsigned char *start,
261                                  const char *text, size_t text_len)
262{
263    return mbedtls_asn1_write_tagged_string(p, start, MBEDTLS_ASN1_IA5_STRING, text, text_len);
264}
265
266int mbedtls_asn1_write_named_bitstring(unsigned char **p,
267                                       const unsigned char *start,
268                                       const unsigned char *buf,
269                                       size_t bits)
270{
271    size_t unused_bits, byte_len;
272    const unsigned char *cur_byte;
273    unsigned char cur_byte_shifted;
274    unsigned char bit;
275
276    byte_len = (bits + 7) / 8;
277    unused_bits = (byte_len * 8) - bits;
278
279    /*
280     * Named bitstrings require that trailing 0s are excluded in the encoding
281     * of the bitstring. Trailing 0s are considered part of the 'unused' bits
282     * when encoding this value in the first content octet
283     */
284    if (bits != 0) {
285        cur_byte = buf + byte_len - 1;
286        cur_byte_shifted = *cur_byte >> unused_bits;
287
288        for (;;) {
289            bit = cur_byte_shifted & 0x1;
290            cur_byte_shifted >>= 1;
291
292            if (bit != 0) {
293                break;
294            }
295
296            bits--;
297            if (bits == 0) {
298                break;
299            }
300
301            if (bits % 8 == 0) {
302                cur_byte_shifted = *--cur_byte;
303            }
304        }
305    }
306
307    return mbedtls_asn1_write_bitstring(p, start, buf, bits);
308}
309
310int mbedtls_asn1_write_bitstring(unsigned char **p, const unsigned char *start,
311                                 const unsigned char *buf, size_t bits)
312{
313    size_t len = 0;
314    size_t unused_bits, byte_len;
315
316    byte_len = (bits + 7) / 8;
317    unused_bits = (byte_len * 8) - bits;
318
319    if (*p < start || (size_t) (*p - start) < byte_len + 1) {
320        return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
321    }
322
323    len = byte_len + 1;
324
325    /* Write the bitstring. Ensure the unused bits are zeroed */
326    if (byte_len > 0) {
327        byte_len--;
328        *--(*p) = buf[byte_len] & ~((0x1 << unused_bits) - 1);
329        (*p) -= byte_len;
330        memcpy(*p, buf, byte_len);
331    }
332
333    /* Write unused bits */
334    *--(*p) = (unsigned char) unused_bits;
335
336    return mbedtls_asn1_write_len_and_tag(p, start, len, MBEDTLS_ASN1_BIT_STRING);
337}
338
339int mbedtls_asn1_write_octet_string(unsigned char **p, const unsigned char *start,
340                                    const unsigned char *buf, size_t size)
341{
342    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
343    size_t len = 0;
344
345    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, buf, size));
346
347    return mbedtls_asn1_write_len_and_tag(p, start, len, MBEDTLS_ASN1_OCTET_STRING);
348}
349
350
351#if !defined(MBEDTLS_ASN1_PARSE_C)
352/* This is a copy of the ASN.1 parsing function mbedtls_asn1_find_named_data(),
353 * which is replicated to avoid a dependency ASN1_WRITE_C on ASN1_PARSE_C. */
354static mbedtls_asn1_named_data *asn1_find_named_data(
355    mbedtls_asn1_named_data *list,
356    const char *oid, size_t len)
357{
358    while (list != NULL) {
359        if (list->oid.len == len &&
360            memcmp(list->oid.p, oid, len) == 0) {
361            break;
362        }
363
364        list = list->next;
365    }
366
367    return list;
368}
369#else
370#define asn1_find_named_data(list, oid, len) \
371    ((mbedtls_asn1_named_data *) mbedtls_asn1_find_named_data(list, oid, len))
372#endif
373
374mbedtls_asn1_named_data *mbedtls_asn1_store_named_data(
375    mbedtls_asn1_named_data **head,
376    const char *oid, size_t oid_len,
377    const unsigned char *val,
378    size_t val_len)
379{
380    mbedtls_asn1_named_data *cur;
381
382    if ((cur = asn1_find_named_data(*head, oid, oid_len)) == NULL) {
383        // Add new entry if not present yet based on OID
384        //
385        cur = (mbedtls_asn1_named_data *) mbedtls_calloc(1,
386                                                         sizeof(mbedtls_asn1_named_data));
387        if (cur == NULL) {
388            return NULL;
389        }
390
391        cur->oid.len = oid_len;
392        cur->oid.p = mbedtls_calloc(1, oid_len);
393        if (cur->oid.p == NULL) {
394            mbedtls_free(cur);
395            return NULL;
396        }
397
398        memcpy(cur->oid.p, oid, oid_len);
399
400        cur->val.len = val_len;
401        if (val_len != 0) {
402            cur->val.p = mbedtls_calloc(1, val_len);
403            if (cur->val.p == NULL) {
404                mbedtls_free(cur->oid.p);
405                mbedtls_free(cur);
406                return NULL;
407            }
408        }
409
410        cur->next = *head;
411        *head = cur;
412    } else if (val_len == 0) {
413        mbedtls_free(cur->val.p);
414        cur->val.p = NULL;
415    } else if (cur->val.len != val_len) {
416        /*
417         * Enlarge existing value buffer if needed
418         * Preserve old data until the allocation succeeded, to leave list in
419         * a consistent state in case allocation fails.
420         */
421        void *p = mbedtls_calloc(1, val_len);
422        if (p == NULL) {
423            return NULL;
424        }
425
426        mbedtls_free(cur->val.p);
427        cur->val.p = p;
428        cur->val.len = val_len;
429    }
430
431    if (val != NULL && val_len != 0) {
432        memcpy(cur->val.p, val, val_len);
433    }
434
435    return cur;
436}
437#endif /* MBEDTLS_ASN1_WRITE_C */
438