quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

x509_create.c (19934B)


      1 /*
      2  *  X.509 base functions for creating certificates / CSRs
      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_X509_CREATE_C)
     11 
     12 #include "x509_internal.h"
     13 #include "mbedtls/asn1write.h"
     14 #include "mbedtls/error.h"
     15 #include "mbedtls/oid.h"
     16 
     17 #include <string.h>
     18 
     19 #include "mbedtls/platform.h"
     20 
     21 #include "mbedtls/asn1.h"
     22 
     23 /* Structure linking OIDs for X.509 DN AttributeTypes to their
     24  * string representations and default string encodings used by Mbed TLS. */
     25 typedef struct {
     26     const char *name; /* String representation of AttributeType, e.g.
     27                        * "CN" or "emailAddress". */
     28     size_t name_len; /* Length of 'name', without trailing 0 byte. */
     29     const char *oid; /* String representation of OID of AttributeType,
     30                       * as per RFC 5280, Appendix A.1. encoded as per
     31                       * X.690 */
     32     int default_tag; /* The default character encoding used for the
     33                       * given attribute type, e.g.
     34                       * MBEDTLS_ASN1_UTF8_STRING for UTF-8. */
     35 } x509_attr_descriptor_t;
     36 
     37 #define ADD_STRLEN(s)     s, sizeof(s) - 1
     38 
     39 /* X.509 DN attributes from RFC 5280, Appendix A.1. */
     40 static const x509_attr_descriptor_t x509_attrs[] =
     41 {
     42     { ADD_STRLEN("CN"),
     43       MBEDTLS_OID_AT_CN, MBEDTLS_ASN1_UTF8_STRING },
     44     { ADD_STRLEN("commonName"),
     45       MBEDTLS_OID_AT_CN, MBEDTLS_ASN1_UTF8_STRING },
     46     { ADD_STRLEN("C"),
     47       MBEDTLS_OID_AT_COUNTRY, MBEDTLS_ASN1_PRINTABLE_STRING },
     48     { ADD_STRLEN("countryName"),
     49       MBEDTLS_OID_AT_COUNTRY, MBEDTLS_ASN1_PRINTABLE_STRING },
     50     { ADD_STRLEN("O"),
     51       MBEDTLS_OID_AT_ORGANIZATION, MBEDTLS_ASN1_UTF8_STRING },
     52     { ADD_STRLEN("organizationName"),
     53       MBEDTLS_OID_AT_ORGANIZATION, MBEDTLS_ASN1_UTF8_STRING },
     54     { ADD_STRLEN("L"),
     55       MBEDTLS_OID_AT_LOCALITY, MBEDTLS_ASN1_UTF8_STRING },
     56     { ADD_STRLEN("locality"),
     57       MBEDTLS_OID_AT_LOCALITY, MBEDTLS_ASN1_UTF8_STRING },
     58     { ADD_STRLEN("R"),
     59       MBEDTLS_OID_PKCS9_EMAIL, MBEDTLS_ASN1_IA5_STRING },
     60     { ADD_STRLEN("OU"),
     61       MBEDTLS_OID_AT_ORG_UNIT, MBEDTLS_ASN1_UTF8_STRING },
     62     { ADD_STRLEN("organizationalUnitName"),
     63       MBEDTLS_OID_AT_ORG_UNIT, MBEDTLS_ASN1_UTF8_STRING },
     64     { ADD_STRLEN("ST"),
     65       MBEDTLS_OID_AT_STATE, MBEDTLS_ASN1_UTF8_STRING },
     66     { ADD_STRLEN("stateOrProvinceName"),
     67       MBEDTLS_OID_AT_STATE, MBEDTLS_ASN1_UTF8_STRING },
     68     { ADD_STRLEN("emailAddress"),
     69       MBEDTLS_OID_PKCS9_EMAIL, MBEDTLS_ASN1_IA5_STRING },
     70     { ADD_STRLEN("serialNumber"),
     71       MBEDTLS_OID_AT_SERIAL_NUMBER, MBEDTLS_ASN1_PRINTABLE_STRING },
     72     { ADD_STRLEN("postalAddress"),
     73       MBEDTLS_OID_AT_POSTAL_ADDRESS, MBEDTLS_ASN1_PRINTABLE_STRING },
     74     { ADD_STRLEN("postalCode"),
     75       MBEDTLS_OID_AT_POSTAL_CODE, MBEDTLS_ASN1_PRINTABLE_STRING },
     76     { ADD_STRLEN("dnQualifier"),
     77       MBEDTLS_OID_AT_DN_QUALIFIER, MBEDTLS_ASN1_PRINTABLE_STRING },
     78     { ADD_STRLEN("title"),
     79       MBEDTLS_OID_AT_TITLE, MBEDTLS_ASN1_UTF8_STRING },
     80     { ADD_STRLEN("surName"),
     81       MBEDTLS_OID_AT_SUR_NAME, MBEDTLS_ASN1_UTF8_STRING },
     82     { ADD_STRLEN("SN"),
     83       MBEDTLS_OID_AT_SUR_NAME, MBEDTLS_ASN1_UTF8_STRING },
     84     { ADD_STRLEN("givenName"),
     85       MBEDTLS_OID_AT_GIVEN_NAME, MBEDTLS_ASN1_UTF8_STRING },
     86     { ADD_STRLEN("GN"),
     87       MBEDTLS_OID_AT_GIVEN_NAME, MBEDTLS_ASN1_UTF8_STRING },
     88     { ADD_STRLEN("initials"),
     89       MBEDTLS_OID_AT_INITIALS, MBEDTLS_ASN1_UTF8_STRING },
     90     { ADD_STRLEN("pseudonym"),
     91       MBEDTLS_OID_AT_PSEUDONYM, MBEDTLS_ASN1_UTF8_STRING },
     92     { ADD_STRLEN("generationQualifier"),
     93       MBEDTLS_OID_AT_GENERATION_QUALIFIER, MBEDTLS_ASN1_UTF8_STRING },
     94     { ADD_STRLEN("domainComponent"),
     95       MBEDTLS_OID_DOMAIN_COMPONENT, MBEDTLS_ASN1_IA5_STRING },
     96     { ADD_STRLEN("DC"),
     97       MBEDTLS_OID_DOMAIN_COMPONENT,   MBEDTLS_ASN1_IA5_STRING },
     98     { NULL, 0, NULL, MBEDTLS_ASN1_NULL }
     99 };
    100 
    101 static const x509_attr_descriptor_t *x509_attr_descr_from_name(const char *name, size_t name_len)
    102 {
    103     const x509_attr_descriptor_t *cur;
    104 
    105     for (cur = x509_attrs; cur->name != NULL; cur++) {
    106         if (cur->name_len == name_len &&
    107             strncmp(cur->name, name, name_len) == 0) {
    108             break;
    109         }
    110     }
    111 
    112     if (cur->name == NULL) {
    113         return NULL;
    114     }
    115 
    116     return cur;
    117 }
    118 
    119 static int hex_to_int(char c)
    120 {
    121     return ('0' <= c && c <= '9') ? (c - '0') :
    122            ('a' <= c && c <= 'f') ? (c - 'a' + 10) :
    123            ('A' <= c && c <= 'F') ? (c - 'A' + 10) : -1;
    124 }
    125 
    126 static int hexpair_to_int(const char *hexpair)
    127 {
    128     int n1 = hex_to_int(*hexpair);
    129     int n2 = hex_to_int(*(hexpair + 1));
    130 
    131     if (n1 != -1 && n2 != -1) {
    132         return (n1 << 4) | n2;
    133     } else {
    134         return -1;
    135     }
    136 }
    137 
    138 static int parse_attribute_value_string(const char *s,
    139                                         int len,
    140                                         unsigned char *data,
    141                                         size_t *data_len)
    142 {
    143     const char *c;
    144     const char *end = s + len;
    145     unsigned char *d = data;
    146     int n;
    147 
    148     for (c = s; c < end; c++) {
    149         if (*c == '\\') {
    150             c++;
    151 
    152             /* Check for valid escaped characters as per RFC 4514 Section 3 */
    153             if (c + 1 < end && (n = hexpair_to_int(c)) != -1) {
    154                 if (n == 0) {
    155                     return MBEDTLS_ERR_X509_INVALID_NAME;
    156                 }
    157                 *(d++) = n;
    158                 c++;
    159             } else if (c < end && strchr(" ,=+<>#;\"\\", *c)) {
    160                 *(d++) = *c;
    161             } else {
    162                 return MBEDTLS_ERR_X509_INVALID_NAME;
    163             }
    164         } else {
    165             *(d++) = *c;
    166         }
    167 
    168         if (d - data == MBEDTLS_X509_MAX_DN_NAME_SIZE) {
    169             return MBEDTLS_ERR_X509_INVALID_NAME;
    170         }
    171     }
    172     *data_len = (size_t) (d - data);
    173     return 0;
    174 }
    175 
    176 /** Parse a hexstring containing a DER-encoded string.
    177  *
    178  * \param s         A string of \p len bytes hexadecimal digits.
    179  * \param len       Number of bytes to read from \p s.
    180  * \param data      Output buffer of size \p data_size.
    181  *                  On success, it contains the payload that's DER-encoded
    182  *                  in the input (content without the tag and length).
    183  *                  If the DER tag is a string tag, the payload is guaranteed
    184  *                  not to contain null bytes.
    185  * \param data_size Length of the \p data buffer.
    186  * \param data_len  On success, the length of the parsed string.
    187  *                  It is guaranteed to be less than
    188  *                  #MBEDTLS_X509_MAX_DN_NAME_SIZE.
    189  * \param tag       The ASN.1 tag that the payload in \p data is encoded in.
    190  *
    191  * \retval          0 on success.
    192  * \retval          #MBEDTLS_ERR_X509_INVALID_NAME if \p s does not contain
    193  *                  a valid hexstring,
    194  *                  or if the decoded hexstring is not valid DER,
    195  *                  or if the payload does not fit in \p data,
    196  *                  or if the payload is more than
    197  *                  #MBEDTLS_X509_MAX_DN_NAME_SIZE bytes,
    198  *                  of if \p *tag is an ASN.1 string tag and the payload
    199  *                  contains a null byte.
    200  * \retval          #MBEDTLS_ERR_X509_ALLOC_FAILED on low memory.
    201  */
    202 static int parse_attribute_value_hex_der_encoded(const char *s,
    203                                                  size_t len,
    204                                                  unsigned char *data,
    205                                                  size_t data_size,
    206                                                  size_t *data_len,
    207                                                  int *tag)
    208 {
    209     /* Step 1: preliminary length checks. */
    210     /* Each byte is encoded by exactly two hexadecimal digits. */
    211     if (len % 2 != 0) {
    212         /* Odd number of hex digits */
    213         return MBEDTLS_ERR_X509_INVALID_NAME;
    214     }
    215     size_t const der_length = len / 2;
    216     if (der_length > MBEDTLS_X509_MAX_DN_NAME_SIZE + 4) {
    217         /* The payload would be more than MBEDTLS_X509_MAX_DN_NAME_SIZE
    218          * (after subtracting the ASN.1 tag and length). Reject this early
    219          * to avoid allocating a large intermediate buffer. */
    220         return MBEDTLS_ERR_X509_INVALID_NAME;
    221     }
    222     if (der_length < 1) {
    223         /* Avoid empty-buffer shenanigans. A valid DER encoding is never
    224          * empty. */
    225         return MBEDTLS_ERR_X509_INVALID_NAME;
    226     }
    227 
    228     /* Step 2: Decode the hex string into an intermediate buffer. */
    229     unsigned char *der = mbedtls_calloc(1, der_length);
    230     if (der == NULL) {
    231         return MBEDTLS_ERR_X509_ALLOC_FAILED;
    232     }
    233     /* Beyond this point, der needs to be freed on exit. */
    234     for (size_t i = 0; i < der_length; i++) {
    235         int c = hexpair_to_int(s + 2 * i);
    236         if (c < 0) {
    237             goto error;
    238         }
    239         der[i] = c;
    240     }
    241 
    242     /* Step 3: decode the DER. */
    243     /* We've checked that der_length >= 1 above. */
    244     *tag = der[0];
    245     {
    246         unsigned char *p = der + 1;
    247         if (mbedtls_asn1_get_len(&p, der + der_length, data_len) != 0) {
    248             goto error;
    249         }
    250         /* Now p points to the first byte of the payload inside der,
    251          * and *data_len is the length of the payload. */
    252 
    253         /* Step 4: payload validation */
    254         if (*data_len > MBEDTLS_X509_MAX_DN_NAME_SIZE) {
    255             goto error;
    256         }
    257         /* Strings must not contain null bytes. */
    258         if (MBEDTLS_ASN1_IS_STRING_TAG(*tag)) {
    259             for (size_t i = 0; i < *data_len; i++) {
    260                 if (p[i] == 0) {
    261                     goto error;
    262                 }
    263             }
    264         }
    265 
    266         /* Step 5: output the payload. */
    267         if (*data_len > data_size) {
    268             goto error;
    269         }
    270         memcpy(data, p, *data_len);
    271     }
    272     mbedtls_free(der);
    273 
    274     return 0;
    275 
    276 error:
    277     mbedtls_free(der);
    278     return MBEDTLS_ERR_X509_INVALID_NAME;
    279 }
    280 
    281 int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name)
    282 {
    283     int ret = MBEDTLS_ERR_X509_INVALID_NAME;
    284     int parse_ret = 0;
    285     const char *s = name, *c = s;
    286     const char *end = s + strlen(s);
    287     mbedtls_asn1_buf oid = { .p = NULL, .len = 0, .tag = MBEDTLS_ASN1_NULL };
    288     const x509_attr_descriptor_t *attr_descr = NULL;
    289     int in_attr_type = 1;
    290     int tag;
    291     int numericoid = 0;
    292     unsigned char data[MBEDTLS_X509_MAX_DN_NAME_SIZE];
    293     size_t data_len = 0;
    294 
    295     /* Ensure the output parameter is not already populated.
    296      * (If it were, overwriting it would likely cause a memory leak.)
    297      */
    298     if (*head != NULL) {
    299         return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
    300     }
    301 
    302     while (c <= end) {
    303         if (in_attr_type && *c == '=') {
    304             if ((attr_descr = x509_attr_descr_from_name(s, (size_t) (c - s))) == NULL) {
    305                 if ((mbedtls_oid_from_numeric_string(&oid, s, (size_t) (c - s))) != 0) {
    306                     return MBEDTLS_ERR_X509_INVALID_NAME;
    307                 } else {
    308                     numericoid = 1;
    309                 }
    310             } else {
    311                 oid.len = strlen(attr_descr->oid);
    312                 oid.p = mbedtls_calloc(1, oid.len);
    313                 memcpy(oid.p, attr_descr->oid, oid.len);
    314                 numericoid = 0;
    315             }
    316 
    317             s = c + 1;
    318             in_attr_type = 0;
    319         }
    320 
    321         if (!in_attr_type && ((*c == ',' && *(c-1) != '\\') || c == end)) {
    322             if (s == c) {
    323                 mbedtls_free(oid.p);
    324                 return MBEDTLS_ERR_X509_INVALID_NAME;
    325             } else if (*s == '#') {
    326                 /* We know that c >= s (loop invariant) and c != s (in this
    327                  * else branch), hence c - s - 1 >= 0. */
    328                 parse_ret = parse_attribute_value_hex_der_encoded(
    329                     s + 1, (size_t) (c - s) - 1,
    330                     data, sizeof(data), &data_len, &tag);
    331                 if (parse_ret != 0) {
    332                     mbedtls_free(oid.p);
    333                     return parse_ret;
    334                 }
    335             } else {
    336                 if (numericoid) {
    337                     mbedtls_free(oid.p);
    338                     return MBEDTLS_ERR_X509_INVALID_NAME;
    339                 } else {
    340                     if ((parse_ret =
    341                              parse_attribute_value_string(s, (int) (c - s), data,
    342                                                           &data_len)) != 0) {
    343                         mbedtls_free(oid.p);
    344                         return parse_ret;
    345                     }
    346                     tag = attr_descr->default_tag;
    347                 }
    348             }
    349 
    350             mbedtls_asn1_named_data *cur =
    351                 mbedtls_asn1_store_named_data(head, (char *) oid.p, oid.len,
    352                                               (unsigned char *) data,
    353                                               data_len);
    354             mbedtls_free(oid.p);
    355             oid.p = NULL;
    356             if (cur == NULL) {
    357                 return MBEDTLS_ERR_X509_ALLOC_FAILED;
    358             }
    359 
    360             // set tagType
    361             cur->val.tag = tag;
    362 
    363             while (c < end && *(c + 1) == ' ') {
    364                 c++;
    365             }
    366 
    367             s = c + 1;
    368             in_attr_type = 1;
    369 
    370             /* Successfully parsed one name, update ret to success */
    371             ret = 0;
    372         }
    373         c++;
    374     }
    375     if (oid.p != NULL) {
    376         mbedtls_free(oid.p);
    377     }
    378     return ret;
    379 }
    380 
    381 /* The first byte of the value in the mbedtls_asn1_named_data structure is reserved
    382  * to store the critical boolean for us
    383  */
    384 int mbedtls_x509_set_extension(mbedtls_asn1_named_data **head, const char *oid, size_t oid_len,
    385                                int critical, const unsigned char *val, size_t val_len)
    386 {
    387     mbedtls_asn1_named_data *cur;
    388 
    389     if (val_len > (SIZE_MAX  - 1)) {
    390         return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
    391     }
    392 
    393     if ((cur = mbedtls_asn1_store_named_data(head, oid, oid_len,
    394                                              NULL, val_len + 1)) == NULL) {
    395         return MBEDTLS_ERR_X509_ALLOC_FAILED;
    396     }
    397 
    398     cur->val.p[0] = critical;
    399     memcpy(cur->val.p + 1, val, val_len);
    400 
    401     return 0;
    402 }
    403 
    404 /*
    405  *  RelativeDistinguishedName ::=
    406  *    SET OF AttributeTypeAndValue
    407  *
    408  *  AttributeTypeAndValue ::= SEQUENCE {
    409  *    type     AttributeType,
    410  *    value    AttributeValue }
    411  *
    412  *  AttributeType ::= OBJECT IDENTIFIER
    413  *
    414  *  AttributeValue ::= ANY DEFINED BY AttributeType
    415  */
    416 static int x509_write_name(unsigned char **p,
    417                            unsigned char *start,
    418                            mbedtls_asn1_named_data *cur_name)
    419 {
    420     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    421     size_t len = 0;
    422     const char *oid             = (const char *) cur_name->oid.p;
    423     size_t oid_len              = cur_name->oid.len;
    424     const unsigned char *name   = cur_name->val.p;
    425     size_t name_len             = cur_name->val.len;
    426 
    427     // Write correct string tag and value
    428     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tagged_string(p, start,
    429                                                                cur_name->val.tag,
    430                                                                (const char *) name,
    431                                                                name_len));
    432     // Write OID
    433     //
    434     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid,
    435                                                      oid_len));
    436 
    437     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
    438     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start,
    439                                                      MBEDTLS_ASN1_CONSTRUCTED |
    440                                                      MBEDTLS_ASN1_SEQUENCE));
    441 
    442     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
    443     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start,
    444                                                      MBEDTLS_ASN1_CONSTRUCTED |
    445                                                      MBEDTLS_ASN1_SET));
    446 
    447     return (int) len;
    448 }
    449 
    450 int mbedtls_x509_write_names(unsigned char **p, unsigned char *start,
    451                              mbedtls_asn1_named_data *first)
    452 {
    453     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    454     size_t len = 0;
    455     mbedtls_asn1_named_data *cur = first;
    456 
    457     while (cur != NULL) {
    458         MBEDTLS_ASN1_CHK_ADD(len, x509_write_name(p, start, cur));
    459         cur = cur->next;
    460     }
    461 
    462     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
    463     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED |
    464                                                      MBEDTLS_ASN1_SEQUENCE));
    465 
    466     return (int) len;
    467 }
    468 
    469 int mbedtls_x509_write_sig(unsigned char **p, unsigned char *start,
    470                            const char *oid, size_t oid_len,
    471                            unsigned char *sig, size_t size,
    472                            mbedtls_pk_type_t pk_alg)
    473 {
    474     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    475     int write_null_par;
    476     size_t len = 0;
    477 
    478     if (*p < start || (size_t) (*p - start) < size) {
    479         return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
    480     }
    481 
    482     len = size;
    483     (*p) -= len;
    484     memcpy(*p, sig, len);
    485 
    486     if (*p - start < 1) {
    487         return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
    488     }
    489 
    490     *--(*p) = 0;
    491     len += 1;
    492 
    493     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
    494     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_BIT_STRING));
    495 
    496     // Write OID
    497     //
    498     if (pk_alg == MBEDTLS_PK_ECDSA) {
    499         /*
    500          * The AlgorithmIdentifier's parameters field must be absent for DSA/ECDSA signature
    501          * algorithms, see https://www.rfc-editor.org/rfc/rfc5480#page-17 and
    502          * https://www.rfc-editor.org/rfc/rfc5758#section-3.
    503          */
    504         write_null_par = 0;
    505     } else {
    506         write_null_par = 1;
    507     }
    508     MBEDTLS_ASN1_CHK_ADD(len,
    509                          mbedtls_asn1_write_algorithm_identifier_ext(p, start, oid, oid_len,
    510                                                                      0, write_null_par));
    511 
    512     return (int) len;
    513 }
    514 
    515 static int x509_write_extension(unsigned char **p, unsigned char *start,
    516                                 mbedtls_asn1_named_data *ext)
    517 {
    518     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    519     size_t len = 0;
    520 
    521     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, ext->val.p + 1,
    522                                                             ext->val.len - 1));
    523     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, ext->val.len - 1));
    524     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OCTET_STRING));
    525 
    526     if (ext->val.p[0] != 0) {
    527         MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_bool(p, start, 1));
    528     }
    529 
    530     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, ext->oid.p,
    531                                                             ext->oid.len));
    532     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, ext->oid.len));
    533     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OID));
    534 
    535     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len));
    536     MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED |
    537                                                      MBEDTLS_ASN1_SEQUENCE));
    538 
    539     return (int) len;
    540 }
    541 
    542 /*
    543  * Extension  ::=  SEQUENCE  {
    544  *     extnID      OBJECT IDENTIFIER,
    545  *     critical    BOOLEAN DEFAULT FALSE,
    546  *     extnValue   OCTET STRING
    547  *                 -- contains the DER encoding of an ASN.1 value
    548  *                 -- corresponding to the extension type identified
    549  *                 -- by extnID
    550  *     }
    551  */
    552 int mbedtls_x509_write_extensions(unsigned char **p, unsigned char *start,
    553                                   mbedtls_asn1_named_data *first)
    554 {
    555     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
    556     size_t len = 0;
    557     mbedtls_asn1_named_data *cur_ext = first;
    558 
    559     while (cur_ext != NULL) {
    560         MBEDTLS_ASN1_CHK_ADD(len, x509_write_extension(p, start, cur_ext));
    561         cur_ext = cur_ext->next;
    562     }
    563 
    564     return (int) len;
    565 }
    566 
    567 #endif /* MBEDTLS_X509_CREATE_C */