Home | History | Annotate | Line # | Download | only in property
      1 /*
      2  * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
      3  * Copyright (c) 2019, Oracle and/or its affiliates.  All rights reserved.
      4  *
      5  * Licensed under the Apache License 2.0 (the "License").  You may not use
      6  * this file except in compliance with the License.  You can obtain a copy
      7  * in the file LICENSE in the source distribution or at
      8  * https://www.openssl.org/source/license.html
      9  */
     10 
     11 #include <string.h>
     12 #include <stdio.h>
     13 #include <stdarg.h>
     14 #include <openssl/err.h>
     15 #include "internal/propertyerr.h"
     16 #include "internal/property.h"
     17 #include "internal/numbers.h"
     18 #include "crypto/ctype.h"
     19 #include "internal/nelem.h"
     20 #include "property_local.h"
     21 #include "internal/e_os.h"
     22 
     23 DEFINE_STACK_OF(OSSL_PROPERTY_DEFINITION)
     24 
     25 static const char *skip_space(const char *s)
     26 {
     27     while (ossl_isspace(*s))
     28         s++;
     29     return s;
     30 }
     31 
     32 static int match_ch(const char *t[], char m)
     33 {
     34     const char *s = *t;
     35 
     36     if (*s == m) {
     37         *t = skip_space(s + 1);
     38         return 1;
     39     }
     40     return 0;
     41 }
     42 
     43 #define MATCH(s, m) match(s, m, sizeof(m) - 1)
     44 
     45 static int match(const char *t[], const char m[], size_t m_len)
     46 {
     47     const char *s = *t;
     48 
     49     if (OPENSSL_strncasecmp(s, m, m_len) == 0) {
     50         *t = skip_space(s + m_len);
     51         return 1;
     52     }
     53     return 0;
     54 }
     55 
     56 static int parse_name(OSSL_LIB_CTX *ctx, const char *t[], int create,
     57     OSSL_PROPERTY_IDX *idx)
     58 {
     59     char name[100];
     60     int err = 0;
     61     size_t i = 0;
     62     const char *s = *t;
     63     int user_name = 0;
     64 
     65     for (;;) {
     66         if (!ossl_isalpha(*s)) {
     67             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER,
     68                 "HERE-->%s", *t);
     69             return 0;
     70         }
     71         do {
     72             if (i < sizeof(name) - 1)
     73                 name[i++] = ossl_tolower(*s);
     74             else
     75                 err = 1;
     76         } while (*++s == '_' || ossl_isalnum(*s));
     77         if (*s != '.')
     78             break;
     79         user_name = 1;
     80         if (i < sizeof(name) - 1)
     81             name[i++] = *s;
     82         else
     83             err = 1;
     84         s++;
     85     }
     86     name[i] = '\0';
     87     if (err) {
     88         ERR_raise_data(ERR_LIB_PROP, PROP_R_NAME_TOO_LONG, "HERE-->%s", *t);
     89         return 0;
     90     }
     91     *t = skip_space(s);
     92     *idx = ossl_property_name(ctx, name, user_name && create);
     93     return 1;
     94 }
     95 
     96 static int parse_number(const char *t[], OSSL_PROPERTY_DEFINITION *res)
     97 {
     98     const char *s = *t;
     99     int64_t v = 0;
    100 
    101     do {
    102         if (!ossl_isdigit(*s)) {
    103             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
    104                 "HERE-->%s", *t);
    105             return 0;
    106         }
    107         /* overflow check */
    108         if (v > ((INT64_MAX - (*s - '0')) / 10)) {
    109             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
    110                 "Property %s overflows", *t);
    111             return 0;
    112         }
    113         v = v * 10 + (*s++ - '0');
    114     } while (ossl_isdigit(*s));
    115     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
    116         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
    117             "HERE-->%s", *t);
    118         return 0;
    119     }
    120     *t = skip_space(s);
    121     res->type = OSSL_PROPERTY_TYPE_NUMBER;
    122     res->v.int_val = v;
    123     return 1;
    124 }
    125 
    126 static int parse_hex(const char *t[], OSSL_PROPERTY_DEFINITION *res)
    127 {
    128     const char *s = *t;
    129     int64_t v = 0;
    130     int sval;
    131 
    132     do {
    133         if (ossl_isdigit(*s)) {
    134             sval = *s - '0';
    135         } else if (ossl_isxdigit(*s)) {
    136             sval = ossl_tolower(*s) - 'a' + 10;
    137         } else {
    138             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
    139                 "%s", *t);
    140             return 0;
    141         }
    142 
    143         if (v > ((INT64_MAX - sval) / 16)) {
    144             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
    145                 "Property %s overflows", *t);
    146             return 0;
    147         }
    148 
    149         v <<= 4;
    150         v += sval;
    151     } while (ossl_isxdigit(*++s));
    152     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
    153         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
    154             "HERE-->%s", *t);
    155         return 0;
    156     }
    157     *t = skip_space(s);
    158     res->type = OSSL_PROPERTY_TYPE_NUMBER;
    159     res->v.int_val = v;
    160     return 1;
    161 }
    162 
    163 static int parse_oct(const char *t[], OSSL_PROPERTY_DEFINITION *res)
    164 {
    165     const char *s = *t;
    166     int64_t v = 0;
    167 
    168     do {
    169         if (*s == '9' || *s == '8' || !ossl_isdigit(*s)) {
    170             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
    171                 "HERE-->%s", *t);
    172             return 0;
    173         }
    174         if (v > ((INT64_MAX - (*s - '0')) / 8)) {
    175             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
    176                 "Property %s overflows", *t);
    177             return 0;
    178         }
    179 
    180         v = (v << 3) + (*s - '0');
    181     } while (ossl_isdigit(*++s) && *s != '9' && *s != '8');
    182     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
    183         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
    184             "HERE-->%s", *t);
    185         return 0;
    186     }
    187     *t = skip_space(s);
    188     res->type = OSSL_PROPERTY_TYPE_NUMBER;
    189     res->v.int_val = v;
    190     return 1;
    191 }
    192 
    193 static int parse_string(OSSL_LIB_CTX *ctx, const char *t[], char delim,
    194     OSSL_PROPERTY_DEFINITION *res, const int create)
    195 {
    196     char v[1000];
    197     const char *s = *t;
    198     size_t i = 0;
    199     int err = 0;
    200 
    201     while (*s != '\0' && *s != delim) {
    202         if (i < sizeof(v) - 1)
    203             v[i++] = *s;
    204         else
    205             err = 1;
    206         s++;
    207     }
    208     if (*s == '\0') {
    209         ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER,
    210             "HERE-->%c%s", delim, *t);
    211         return 0;
    212     }
    213     v[i] = '\0';
    214     if (err) {
    215         ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
    216     } else {
    217         res->v.str_val = ossl_property_value(ctx, v, create);
    218     }
    219     *t = skip_space(s + 1);
    220     res->type = OSSL_PROPERTY_TYPE_STRING;
    221     return !err;
    222 }
    223 
    224 static int parse_unquoted(OSSL_LIB_CTX *ctx, const char *t[],
    225     OSSL_PROPERTY_DEFINITION *res, const int create)
    226 {
    227     char v[1000];
    228     const char *s = *t;
    229     size_t i = 0;
    230     int err = 0;
    231 
    232     if (*s == '\0' || *s == ',')
    233         return 0;
    234     while (ossl_isprint(*s) && !ossl_isspace(*s) && *s != ',') {
    235         if (i < sizeof(v) - 1)
    236             v[i++] = ossl_tolower(*s);
    237         else
    238             err = 1;
    239         s++;
    240     }
    241     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
    242         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER,
    243             "HERE-->%s", s);
    244         return 0;
    245     }
    246     v[i] = 0;
    247     if (err)
    248         ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
    249     else if ((res->v.str_val = ossl_property_value(ctx, v, create)) == 0)
    250         err = 1;
    251     *t = skip_space(s);
    252     res->type = OSSL_PROPERTY_TYPE_STRING;
    253     return !err;
    254 }
    255 
    256 static int parse_value(OSSL_LIB_CTX *ctx, const char *t[],
    257     OSSL_PROPERTY_DEFINITION *res, int create)
    258 {
    259     const char *s = *t;
    260     int r = 0;
    261 
    262     if (*s == '"' || *s == '\'') {
    263         s++;
    264         r = parse_string(ctx, &s, s[-1], res, create);
    265     } else if (*s == '+') {
    266         s++;
    267         r = parse_number(&s, res);
    268     } else if (*s == '-') {
    269         s++;
    270         r = parse_number(&s, res);
    271         res->v.int_val = -res->v.int_val;
    272     } else if (*s == '0' && s[1] == 'x') {
    273         s += 2;
    274         r = parse_hex(&s, res);
    275     } else if (*s == '0' && ossl_isdigit(s[1])) {
    276         s++;
    277         r = parse_oct(&s, res);
    278     } else if (ossl_isdigit(*s)) {
    279         return parse_number(t, res);
    280     } else if (ossl_isalpha(*s))
    281         return parse_unquoted(ctx, t, res, create);
    282     if (r)
    283         *t = s;
    284     return r;
    285 }
    286 
    287 static int pd_compare(const OSSL_PROPERTY_DEFINITION *const *p1,
    288     const OSSL_PROPERTY_DEFINITION *const *p2)
    289 {
    290     const OSSL_PROPERTY_DEFINITION *pd1 = *p1;
    291     const OSSL_PROPERTY_DEFINITION *pd2 = *p2;
    292 
    293     if (pd1->name_idx < pd2->name_idx)
    294         return -1;
    295     if (pd1->name_idx > pd2->name_idx)
    296         return 1;
    297     return 0;
    298 }
    299 
    300 static void pd_free(OSSL_PROPERTY_DEFINITION *pd)
    301 {
    302     OPENSSL_free(pd);
    303 }
    304 
    305 /*
    306  * Convert a stack of property definitions and queries into a fixed array.
    307  * The items are sorted for efficient query.  The stack is not freed.
    308  * This function also checks for duplicated names and returns an error if
    309  * any exist.
    310  */
    311 static OSSL_PROPERTY_LIST *
    312 stack_to_property_list(OSSL_LIB_CTX *ctx,
    313     STACK_OF(OSSL_PROPERTY_DEFINITION) *sk)
    314 {
    315     const int n = sk_OSSL_PROPERTY_DEFINITION_num(sk);
    316     OSSL_PROPERTY_LIST *r;
    317     OSSL_PROPERTY_IDX prev_name_idx = 0;
    318     int i;
    319 
    320     r = OPENSSL_malloc(sizeof(*r)
    321         + (n <= 0 ? 0 : n - 1) * sizeof(r->properties[0]));
    322     if (r != NULL) {
    323         sk_OSSL_PROPERTY_DEFINITION_sort(sk);
    324 
    325         r->has_optional = 0;
    326         for (i = 0; i < n; i++) {
    327             r->properties[i] = *sk_OSSL_PROPERTY_DEFINITION_value(sk, i);
    328             r->has_optional |= r->properties[i].optional;
    329 
    330             /* Check for duplicated names */
    331             if (i > 0 && r->properties[i].name_idx == prev_name_idx) {
    332                 OPENSSL_free(r);
    333                 ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
    334                     "Duplicated name `%s'",
    335                     ossl_property_name_str(ctx, prev_name_idx));
    336                 return NULL;
    337             }
    338             prev_name_idx = r->properties[i].name_idx;
    339         }
    340         r->num_properties = n;
    341     }
    342     return r;
    343 }
    344 
    345 OSSL_PROPERTY_LIST *ossl_parse_property(OSSL_LIB_CTX *ctx, const char *defn)
    346 {
    347     OSSL_PROPERTY_DEFINITION *prop = NULL;
    348     OSSL_PROPERTY_LIST *res = NULL;
    349     STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
    350     const char *s = defn;
    351     int done;
    352 
    353     if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
    354         return NULL;
    355 
    356     s = skip_space(s);
    357     done = *s == '\0';
    358     while (!done) {
    359         const char *start = s;
    360 
    361         prop = OPENSSL_malloc(sizeof(*prop));
    362         if (prop == NULL)
    363             goto err;
    364         memset(&prop->v, 0, sizeof(prop->v));
    365         prop->optional = 0;
    366         if (!parse_name(ctx, &s, 1, &prop->name_idx))
    367             goto err;
    368         prop->oper = OSSL_PROPERTY_OPER_EQ;
    369         if (prop->name_idx == 0) {
    370             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
    371                 "Unknown name HERE-->%s", start);
    372             goto err;
    373         }
    374         if (match_ch(&s, '=')) {
    375             if (!parse_value(ctx, &s, prop, 1)) {
    376                 ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_VALUE,
    377                     "HERE-->%s", start);
    378                 goto err;
    379             }
    380         } else {
    381             /* A name alone means a true Boolean */
    382             prop->type = OSSL_PROPERTY_TYPE_STRING;
    383             prop->v.str_val = OSSL_PROPERTY_TRUE;
    384         }
    385 
    386         if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
    387             goto err;
    388         prop = NULL;
    389         done = !match_ch(&s, ',');
    390     }
    391     if (*s != '\0') {
    392         ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
    393             "HERE-->%s", s);
    394         goto err;
    395     }
    396     res = stack_to_property_list(ctx, sk);
    397 
    398 err:
    399     OPENSSL_free(prop);
    400     sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
    401     return res;
    402 }
    403 
    404 OSSL_PROPERTY_LIST *ossl_parse_query(OSSL_LIB_CTX *ctx, const char *s,
    405     int create_values)
    406 {
    407     STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
    408     OSSL_PROPERTY_LIST *res = NULL;
    409     OSSL_PROPERTY_DEFINITION *prop = NULL;
    410     int done;
    411 
    412     if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
    413         return NULL;
    414 
    415     s = skip_space(s);
    416     done = *s == '\0';
    417     while (!done) {
    418         prop = OPENSSL_malloc(sizeof(*prop));
    419         if (prop == NULL)
    420             goto err;
    421         memset(&prop->v, 0, sizeof(prop->v));
    422 
    423         if (match_ch(&s, '-')) {
    424             prop->oper = OSSL_PROPERTY_OVERRIDE;
    425             prop->optional = 0;
    426             if (!parse_name(ctx, &s, 1, &prop->name_idx))
    427                 goto err;
    428             goto skip_value;
    429         }
    430         prop->optional = match_ch(&s, '?');
    431         if (!parse_name(ctx, &s, 1, &prop->name_idx))
    432             goto err;
    433 
    434         if (match_ch(&s, '=')) {
    435             prop->oper = OSSL_PROPERTY_OPER_EQ;
    436         } else if (MATCH(&s, "!=")) {
    437             prop->oper = OSSL_PROPERTY_OPER_NE;
    438         } else {
    439             /* A name alone is a Boolean comparison for true */
    440             prop->oper = OSSL_PROPERTY_OPER_EQ;
    441             prop->type = OSSL_PROPERTY_TYPE_STRING;
    442             prop->v.str_val = OSSL_PROPERTY_TRUE;
    443             goto skip_value;
    444         }
    445         if (!parse_value(ctx, &s, prop, create_values))
    446             prop->type = OSSL_PROPERTY_TYPE_VALUE_UNDEFINED;
    447 
    448     skip_value:
    449         if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
    450             goto err;
    451         prop = NULL;
    452         done = !match_ch(&s, ',');
    453     }
    454     if (*s != '\0') {
    455         ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
    456             "HERE-->%s", s);
    457         goto err;
    458     }
    459     res = stack_to_property_list(ctx, sk);
    460 
    461 err:
    462     OPENSSL_free(prop);
    463     sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
    464     return res;
    465 }
    466 
    467 /*
    468  * Compare a query against a definition.
    469  * Return the number of clauses matched or -1 if a mandatory clause is false.
    470  */
    471 int ossl_property_match_count(const OSSL_PROPERTY_LIST *query,
    472     const OSSL_PROPERTY_LIST *defn)
    473 {
    474     const OSSL_PROPERTY_DEFINITION *const q = query->properties;
    475     const OSSL_PROPERTY_DEFINITION *const d = defn->properties;
    476     int i = 0, j = 0, matches = 0;
    477     OSSL_PROPERTY_OPER oper;
    478 
    479     while (i < query->num_properties) {
    480         if ((oper = q[i].oper) == OSSL_PROPERTY_OVERRIDE) {
    481             i++;
    482             continue;
    483         }
    484         if (j < defn->num_properties) {
    485             if (q[i].name_idx > d[j].name_idx) { /* skip defn, not in query */
    486                 j++;
    487                 continue;
    488             }
    489             if (q[i].name_idx == d[j].name_idx) { /* both in defn and query */
    490                 const int eq = q[i].type == d[j].type
    491                     && memcmp(&q[i].v, &d[j].v, sizeof(q[i].v)) == 0;
    492 
    493                 if ((eq && oper == OSSL_PROPERTY_OPER_EQ)
    494                     || (!eq && oper == OSSL_PROPERTY_OPER_NE))
    495                     matches++;
    496                 else if (!q[i].optional)
    497                     return -1;
    498                 i++;
    499                 j++;
    500                 continue;
    501             }
    502         }
    503 
    504         /*
    505          * Handle the cases of a missing value and a query with no corresponding
    506          * definition.  The former fails for any comparison except inequality,
    507          * the latter is treated as a comparison against the Boolean false.
    508          */
    509         if (q[i].type == OSSL_PROPERTY_TYPE_VALUE_UNDEFINED) {
    510             if (oper == OSSL_PROPERTY_OPER_NE)
    511                 matches++;
    512             else if (!q[i].optional)
    513                 return -1;
    514         } else if (q[i].type != OSSL_PROPERTY_TYPE_STRING
    515             || (oper == OSSL_PROPERTY_OPER_EQ
    516                 && q[i].v.str_val != OSSL_PROPERTY_FALSE)
    517             || (oper == OSSL_PROPERTY_OPER_NE
    518                 && q[i].v.str_val == OSSL_PROPERTY_FALSE)) {
    519             if (!q[i].optional)
    520                 return -1;
    521         } else {
    522             matches++;
    523         }
    524         i++;
    525     }
    526     return matches;
    527 }
    528 
    529 void ossl_property_free(OSSL_PROPERTY_LIST *p)
    530 {
    531     OPENSSL_free(p);
    532 }
    533 
    534 /*
    535  * Merge two property lists.
    536  * If there is a common name, the one from the first list is used.
    537  */
    538 OSSL_PROPERTY_LIST *ossl_property_merge(const OSSL_PROPERTY_LIST *a,
    539     const OSSL_PROPERTY_LIST *b)
    540 {
    541     const OSSL_PROPERTY_DEFINITION *const ap = a->properties;
    542     const OSSL_PROPERTY_DEFINITION *const bp = b->properties;
    543     const OSSL_PROPERTY_DEFINITION *copy;
    544     OSSL_PROPERTY_LIST *r;
    545     int i, j, n;
    546     const int t = a->num_properties + b->num_properties;
    547 
    548     r = OPENSSL_malloc(sizeof(*r)
    549         + (t == 0 ? 0 : t - 1) * sizeof(r->properties[0]));
    550     if (r == NULL)
    551         return NULL;
    552 
    553     r->has_optional = 0;
    554     for (i = j = n = 0; i < a->num_properties || j < b->num_properties; n++) {
    555         if (i >= a->num_properties) {
    556             copy = &bp[j++];
    557         } else if (j >= b->num_properties) {
    558             copy = &ap[i++];
    559         } else if (ap[i].name_idx <= bp[j].name_idx) {
    560             if (ap[i].name_idx == bp[j].name_idx)
    561                 j++;
    562             copy = &ap[i++];
    563         } else {
    564             copy = &bp[j++];
    565         }
    566         memcpy(r->properties + n, copy, sizeof(r->properties[0]));
    567         r->has_optional |= copy->optional;
    568     }
    569     r->num_properties = n;
    570 
    571     return r;
    572 }
    573 
    574 int ossl_property_parse_init(OSSL_LIB_CTX *ctx)
    575 {
    576     static const char *const predefined_names[] = {
    577         "provider", /* Name of provider (default, legacy, fips) */
    578         "version", /* Version number of this provider */
    579         "fips", /* FIPS validated or FIPS supporting algorithm */
    580         "output", /* Output type for encoders */
    581         "input", /* Input type for decoders */
    582         "structure", /* Structure name for encoders and decoders */
    583     };
    584     size_t i;
    585 
    586     for (i = 0; i < OSSL_NELEM(predefined_names); i++)
    587         if (ossl_property_name(ctx, predefined_names[i], 1) == 0)
    588             goto err;
    589 
    590     /*
    591      * Pre-populate the two Boolean values. We must do them before any other
    592      * values and in this order so that we get the same index as the global
    593      * OSSL_PROPERTY_TRUE and OSSL_PROPERTY_FALSE values
    594      */
    595     if ((ossl_property_value(ctx, "yes", 1) != OSSL_PROPERTY_TRUE)
    596         || (ossl_property_value(ctx, "no", 1) != OSSL_PROPERTY_FALSE))
    597         goto err;
    598 
    599     return 1;
    600 err:
    601     return 0;
    602 }
    603 
    604 static void put_char(char ch, char **buf, size_t *remain, size_t *needed)
    605 {
    606     if (*remain == 0) {
    607         ++*needed;
    608         return;
    609     }
    610     if (*remain == 1)
    611         **buf = '\0';
    612     else
    613         **buf = ch;
    614     ++*buf;
    615     ++*needed;
    616     --*remain;
    617 }
    618 
    619 static void put_str(const char *str, char **buf, size_t *remain, size_t *needed)
    620 {
    621     size_t olen, len, i;
    622     char quote = '\0';
    623     int quotes;
    624 
    625     len = olen = strlen(str);
    626     *needed += len;
    627 
    628     /*
    629      * Check to see if we need quotes or not.
    630      * Characters that are legal in a PropertyName don't need quoting.
    631      * We simply assume all others require quotes.
    632      */
    633     for (i = 0; i < len; i++)
    634         if (!ossl_isalnum(str[i]) && str[i] != '.' && str[i] != '_') {
    635             /* Default to single quotes ... */
    636             if (quote == '\0')
    637                 quote = '\'';
    638             /* ... but use double quotes if a single is present */
    639             if (str[i] == '\'')
    640                 quote = '"';
    641         }
    642 
    643     quotes = quote != '\0';
    644     if (*remain <= (size_t)quotes) {
    645         *needed += 2 * quotes;
    646         return;
    647     }
    648 
    649     if (quotes)
    650         put_char(quote, buf, remain, needed);
    651 
    652     if (*remain < len + 1 + quotes)
    653         len = *remain - 1;
    654 
    655     if (len > 0) {
    656         memcpy(*buf, str, len);
    657         *buf += len;
    658         *remain -= len;
    659     }
    660 
    661     if (quotes)
    662         put_char(quote, buf, remain, needed);
    663 
    664     if (len < olen && *remain == 1) {
    665         **buf = '\0';
    666         ++*buf;
    667         --*remain;
    668     }
    669 }
    670 
    671 static void put_num(int64_t val, char **buf, size_t *remain, size_t *needed)
    672 {
    673     int64_t tmpval = val;
    674     size_t len = 1;
    675 
    676     if (tmpval < 0) {
    677         len++;
    678         tmpval = -tmpval;
    679     }
    680     for (; tmpval > 9; len++, tmpval /= 10)
    681         ;
    682 
    683     *needed += len;
    684 
    685     if (*remain == 0)
    686         return;
    687 
    688     BIO_snprintf(*buf, *remain, "%lld", (long long int)val);
    689     if (*remain < len) {
    690         *buf += *remain;
    691         *remain = 0;
    692     } else {
    693         *buf += len;
    694         *remain -= len;
    695     }
    696 }
    697 
    698 size_t ossl_property_list_to_string(OSSL_LIB_CTX *ctx,
    699     const OSSL_PROPERTY_LIST *list, char *buf,
    700     size_t bufsize)
    701 {
    702     int i;
    703     const OSSL_PROPERTY_DEFINITION *prop = NULL;
    704     size_t needed = 0;
    705     const char *val;
    706 
    707     if (list == NULL) {
    708         if (bufsize > 0)
    709             *buf = '\0';
    710         return 1;
    711     }
    712     if (list->num_properties != 0)
    713         prop = &list->properties[list->num_properties - 1];
    714     for (i = 0; i < list->num_properties; i++, prop--) {
    715         /* Skip invalid names */
    716         if (prop->name_idx == 0)
    717             continue;
    718 
    719         if (needed > 0)
    720             put_char(',', &buf, &bufsize, &needed);
    721 
    722         if (prop->optional)
    723             put_char('?', &buf, &bufsize, &needed);
    724         else if (prop->oper == OSSL_PROPERTY_OVERRIDE)
    725             put_char('-', &buf, &bufsize, &needed);
    726 
    727         val = ossl_property_name_str(ctx, prop->name_idx);
    728         if (val == NULL)
    729             return 0;
    730         put_str(val, &buf, &bufsize, &needed);
    731 
    732         switch (prop->oper) {
    733         case OSSL_PROPERTY_OPER_NE:
    734             put_char('!', &buf, &bufsize, &needed);
    735             /* fall through */
    736         case OSSL_PROPERTY_OPER_EQ:
    737             put_char('=', &buf, &bufsize, &needed);
    738             /* put value */
    739             switch (prop->type) {
    740             case OSSL_PROPERTY_TYPE_STRING:
    741                 val = ossl_property_value_str(ctx, prop->v.str_val);
    742                 if (val == NULL)
    743                     return 0;
    744                 put_str(val, &buf, &bufsize, &needed);
    745                 break;
    746 
    747             case OSSL_PROPERTY_TYPE_NUMBER:
    748                 put_num(prop->v.int_val, &buf, &bufsize, &needed);
    749                 break;
    750 
    751             default:
    752                 return 0;
    753             }
    754             break;
    755         default:
    756             /* do nothing */
    757             break;
    758         }
    759     }
    760 
    761     put_char('\0', &buf, &bufsize, &needed);
    762     return needed;
    763 }
    764