xref: /freebsd/crypto/openssl/crypto/property/property_parse.c (revision 44096ebd22ddd0081a357011714eff8963614b65)
1 /*
2  * Copyright 2019-2024 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 "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     if (n != t)
571         r = OPENSSL_realloc(r, sizeof(*r) + (n - 1) * sizeof(r->properties[0]));
572     return r;
573 }
574 
575 int ossl_property_parse_init(OSSL_LIB_CTX *ctx)
576 {
577     static const char *const predefined_names[] = {
578         "provider",     /* Name of provider (default, legacy, fips) */
579         "version",      /* Version number of this provider */
580         "fips",         /* FIPS validated or FIPS supporting algorithm */
581         "output",       /* Output type for encoders */
582         "input",        /* Input type for decoders */
583         "structure",    /* Structure name for encoders and decoders */
584     };
585     size_t i;
586 
587     for (i = 0; i < OSSL_NELEM(predefined_names); i++)
588         if (ossl_property_name(ctx, predefined_names[i], 1) == 0)
589             goto err;
590 
591     /*
592      * Pre-populate the two Boolean values. We must do them before any other
593      * values and in this order so that we get the same index as the global
594      * OSSL_PROPERTY_TRUE and OSSL_PROPERTY_FALSE values
595      */
596     if ((ossl_property_value(ctx, "yes", 1) != OSSL_PROPERTY_TRUE)
597         || (ossl_property_value(ctx, "no", 1) != OSSL_PROPERTY_FALSE))
598         goto err;
599 
600     return 1;
601 err:
602     return 0;
603 }
604 
605 static void put_char(char ch, char **buf, size_t *remain, size_t *needed)
606 {
607     if (*remain == 0) {
608         ++*needed;
609         return;
610     }
611     if (*remain == 1)
612         **buf = '\0';
613     else
614         **buf = ch;
615     ++*buf;
616     ++*needed;
617     --*remain;
618 }
619 
620 static void put_str(const char *str, char **buf, size_t *remain, size_t *needed)
621 {
622     size_t olen, len, i;
623     char quote = '\0';
624     int quotes;
625 
626     len = olen = strlen(str);
627     *needed += len;
628 
629     /*
630      * Check to see if we need quotes or not.
631      * Characters that are legal in a PropertyName don't need quoting.
632      * We simply assume all others require quotes.
633      */
634     for (i = 0; i < len; i++)
635         if (!ossl_isalnum(str[i]) && str[i] != '.' && str[i] != '_') {
636             /* Default to single quotes ... */
637             if (quote == '\0')
638                 quote = '\'';
639             /* ... but use double quotes if a single is present */
640             if (str[i] == '\'')
641                 quote = '"';
642         }
643 
644     quotes = quote != '\0';
645     if (*remain == 0) {
646         *needed += 2 * quotes;
647         return;
648     }
649 
650     if (quotes)
651         put_char(quote, buf, remain, needed);
652 
653     if (*remain < len + 1 + quotes)
654         len = *remain - 1;
655 
656     if (len > 0) {
657         memcpy(*buf, str, len);
658         *buf += len;
659         *remain -= len;
660     }
661 
662     if (quotes)
663         put_char(quote, buf, remain, needed);
664 
665     if (len < olen && *remain == 1) {
666         **buf = '\0';
667         ++*buf;
668         --*remain;
669     }
670 }
671 
672 static void put_num(int64_t val, char **buf, size_t *remain, size_t *needed)
673 {
674     int64_t tmpval = val;
675     size_t len = 1;
676 
677     if (tmpval < 0) {
678         len++;
679         tmpval = -tmpval;
680     }
681     for (; tmpval > 9; len++, tmpval /= 10);
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