1 /*
2  * Copyright 2019-2023 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 "crypto/ctype.h"
18 #include "internal/nelem.h"
19 #include "property_local.h"
20 #include "e_os.h"
21 
22 DEFINE_STACK_OF(OSSL_PROPERTY_DEFINITION)
23 
24 static const char *skip_space(const char *s)
25 {
26     while (ossl_isspace(*s))
27         s++;
28     return s;
29 }
30 
31 static int match_ch(const char *t[], char m)
32 {
33     const char *s = *t;
34 
35     if (*s == m) {
36         *t = skip_space(s + 1);
37         return 1;
38     }
39     return 0;
40 }
41 
42 #define MATCH(s, m) match(s, m, sizeof(m) - 1)
43 
44 static int match(const char *t[], const char m[], size_t m_len)
45 {
46     const char *s = *t;
47 
48     if (OPENSSL_strncasecmp(s, m, m_len) == 0) {
49         *t = skip_space(s + m_len);
50         return 1;
51     }
52     return 0;
53 }
54 
55 static int parse_name(OSSL_LIB_CTX *ctx, const char *t[], int create,
56                       OSSL_PROPERTY_IDX *idx)
57 {
58     char name[100];
59     int err = 0;
60     size_t i = 0;
61     const char *s = *t;
62     int user_name = 0;
63 
64     for (;;) {
65         if (!ossl_isalpha(*s)) {
66             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER,
67                            "HERE-->%s", *t);
68             return 0;
69         }
70         do {
71             if (i < sizeof(name) - 1)
72                 name[i++] = ossl_tolower(*s);
73             else
74                 err = 1;
75         } while (*++s == '_' || ossl_isalnum(*s));
76         if (*s != '.')
77             break;
78         user_name = 1;
79         if (i < sizeof(name) - 1)
80             name[i++] = *s;
81         else
82             err = 1;
83         s++;
84     }
85     name[i] = '\0';
86     if (err) {
87         ERR_raise_data(ERR_LIB_PROP, PROP_R_NAME_TOO_LONG, "HERE-->%s", *t);
88         return 0;
89     }
90     *t = skip_space(s);
91     *idx = ossl_property_name(ctx, name, user_name && create);
92     return 1;
93 }
94 
95 static int parse_number(const char *t[], OSSL_PROPERTY_DEFINITION *res)
96 {
97     const char *s = *t;
98     int64_t v = 0;
99 
100     do {
101         if (!ossl_isdigit(*s)) {
102             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
103                            "HERE-->%s", *t);
104             return 0;
105         }
106         /* overflow check */
107         if (v > ((INT64_MAX - (*s - '0')) / 10)) {
108             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
109                            "Property %s overflows", *t);
110             return 0;
111         }
112         v = v * 10 + (*s++ - '0');
113     } while (ossl_isdigit(*s));
114     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
115         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
116                        "HERE-->%s", *t);
117         return 0;
118     }
119     *t = skip_space(s);
120     res->type = OSSL_PROPERTY_TYPE_NUMBER;
121     res->v.int_val = v;
122     return 1;
123 }
124 
125 static int parse_hex(const char *t[], OSSL_PROPERTY_DEFINITION *res)
126 {
127     const char *s = *t;
128     int64_t v = 0;
129     int sval;
130 
131     do {
132         if (ossl_isdigit(*s)) {
133             sval = *s - '0';
134         } else if (ossl_isxdigit(*s)) {
135             sval = ossl_tolower(*s) - 'a' + 10;
136         } else {
137             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
138                            "%s", *t);
139             return 0;
140         }
141 
142         if (v > ((INT64_MAX - sval) / 16)) {
143             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
144                            "Property %s overflows", *t);
145             return 0;
146         }
147 
148         v <<= 4;
149         v += sval;
150     } while (ossl_isxdigit(*++s));
151     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
152         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
153                        "HERE-->%s", *t);
154         return 0;
155     }
156     *t = skip_space(s);
157     res->type = OSSL_PROPERTY_TYPE_NUMBER;
158     res->v.int_val = v;
159     return 1;
160 }
161 
162 static int parse_oct(const char *t[], OSSL_PROPERTY_DEFINITION *res)
163 {
164     const char *s = *t;
165     int64_t v = 0;
166 
167     do {
168         if (*s == '9' || *s == '8' || !ossl_isdigit(*s)) {
169             ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
170                            "HERE-->%s", *t);
171             return 0;
172         }
173         if (v > ((INT64_MAX - (*s - '0')) / 8)) {
174             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
175                            "Property %s overflows", *t);
176             return 0;
177         }
178 
179         v = (v << 3) + (*s - '0');
180     } while (ossl_isdigit(*++s) && *s != '9' && *s != '8');
181     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
182         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
183                        "HERE-->%s", *t);
184         return 0;
185     }
186     *t = skip_space(s);
187     res->type = OSSL_PROPERTY_TYPE_NUMBER;
188     res->v.int_val = v;
189     return 1;
190 }
191 
192 static int parse_string(OSSL_LIB_CTX *ctx, const char *t[], char delim,
193                         OSSL_PROPERTY_DEFINITION *res, const int create)
194 {
195     char v[1000];
196     const char *s = *t;
197     size_t i = 0;
198     int err = 0;
199 
200     while (*s != '\0' && *s != delim) {
201         if (i < sizeof(v) - 1)
202             v[i++] = *s;
203         else
204             err = 1;
205         s++;
206     }
207     if (*s == '\0') {
208         ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER,
209                        "HERE-->%c%s", delim, *t);
210         return 0;
211     }
212     v[i] = '\0';
213     if (err) {
214         ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
215     } else {
216         res->v.str_val = ossl_property_value(ctx, v, create);
217     }
218     *t = skip_space(s + 1);
219     res->type = OSSL_PROPERTY_TYPE_STRING;
220     return !err;
221 }
222 
223 static int parse_unquoted(OSSL_LIB_CTX *ctx, const char *t[],
224                           OSSL_PROPERTY_DEFINITION *res, const int create)
225 {
226     char v[1000];
227     const char *s = *t;
228     size_t i = 0;
229     int err = 0;
230 
231     if (*s == '\0' || *s == ',')
232         return 0;
233     while (ossl_isprint(*s) && !ossl_isspace(*s) && *s != ',') {
234         if (i < sizeof(v) - 1)
235             v[i++] = ossl_tolower(*s);
236         else
237             err = 1;
238         s++;
239     }
240     if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
241         ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER,
242                        "HERE-->%s", s);
243         return 0;
244     }
245     v[i] = 0;
246     if (err)
247         ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
248     else if ((res->v.str_val = ossl_property_value(ctx, v, create)) == 0)
249         err = 1;
250     *t = skip_space(s);
251     res->type = OSSL_PROPERTY_TYPE_STRING;
252     return !err;
253 }
254 
255 static int parse_value(OSSL_LIB_CTX *ctx, const char *t[],
256                        OSSL_PROPERTY_DEFINITION *res, int create)
257 {
258     const char *s = *t;
259     int r = 0;
260 
261     if (*s == '"' || *s == '\'') {
262         s++;
263         r = parse_string(ctx, &s, s[-1], res, create);
264     } else if (*s == '+') {
265         s++;
266         r = parse_number(&s, res);
267     } else if (*s == '-') {
268         s++;
269         r = parse_number(&s, res);
270         res->v.int_val = -res->v.int_val;
271     } else if (*s == '0' && s[1] == 'x') {
272         s += 2;
273         r = parse_hex(&s, res);
274     } else if (*s == '0' && ossl_isdigit(s[1])) {
275         s++;
276         r = parse_oct(&s, res);
277     } else if (ossl_isdigit(*s)) {
278         return parse_number(t, res);
279     } else if (ossl_isalpha(*s))
280         return parse_unquoted(ctx, t, res, create);
281     if (r)
282         *t = s;
283     return r;
284 }
285 
286 static int pd_compare(const OSSL_PROPERTY_DEFINITION *const *p1,
287                       const OSSL_PROPERTY_DEFINITION *const *p2)
288 {
289     const OSSL_PROPERTY_DEFINITION *pd1 = *p1;
290     const OSSL_PROPERTY_DEFINITION *pd2 = *p2;
291 
292     if (pd1->name_idx < pd2->name_idx)
293         return -1;
294     if (pd1->name_idx > pd2->name_idx)
295         return 1;
296     return 0;
297 }
298 
299 static void pd_free(OSSL_PROPERTY_DEFINITION *pd)
300 {
301     OPENSSL_free(pd);
302 }
303 
304 /*
305  * Convert a stack of property definitions and queries into a fixed array.
306  * The items are sorted for efficient query.  The stack is not freed.
307  * This function also checks for duplicated names and returns an error if
308  * any exist.
309  */
310 static OSSL_PROPERTY_LIST *
311 stack_to_property_list(OSSL_LIB_CTX *ctx,
312                        STACK_OF(OSSL_PROPERTY_DEFINITION) *sk)
313 {
314     const int n = sk_OSSL_PROPERTY_DEFINITION_num(sk);
315     OSSL_PROPERTY_LIST *r;
316     OSSL_PROPERTY_IDX prev_name_idx = 0;
317     int i;
318 
319     r = OPENSSL_malloc(sizeof(*r)
320                        + (n <= 0 ? 0 : n - 1) * sizeof(r->properties[0]));
321     if (r != NULL) {
322         sk_OSSL_PROPERTY_DEFINITION_sort(sk);
323 
324         r->has_optional = 0;
325         for (i = 0; i < n; i++) {
326             r->properties[i] = *sk_OSSL_PROPERTY_DEFINITION_value(sk, i);
327             r->has_optional |= r->properties[i].optional;
328 
329             /* Check for duplicated names */
330             if (i > 0 && r->properties[i].name_idx == prev_name_idx) {
331                 OPENSSL_free(r);
332                 ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
333                                "Duplicated name `%s'",
334                                ossl_property_name_str(ctx, prev_name_idx));
335                 return NULL;
336             }
337             prev_name_idx = r->properties[i].name_idx;
338         }
339         r->num_properties = n;
340     }
341     return r;
342 }
343 
344 OSSL_PROPERTY_LIST *ossl_parse_property(OSSL_LIB_CTX *ctx, const char *defn)
345 {
346     OSSL_PROPERTY_DEFINITION *prop = NULL;
347     OSSL_PROPERTY_LIST *res = NULL;
348     STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
349     const char *s = defn;
350     int done;
351 
352     if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
353         return NULL;
354 
355     s = skip_space(s);
356     done = *s == '\0';
357     while (!done) {
358         const char *start = s;
359 
360         prop = OPENSSL_malloc(sizeof(*prop));
361         if (prop == NULL)
362             goto err;
363         memset(&prop->v, 0, sizeof(prop->v));
364         prop->optional = 0;
365         if (!parse_name(ctx, &s, 1, &prop->name_idx))
366             goto err;
367         prop->oper = OSSL_PROPERTY_OPER_EQ;
368         if (prop->name_idx == 0) {
369             ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
370                            "Unknown name HERE-->%s", start);
371             goto err;
372         }
373         if (match_ch(&s, '=')) {
374             if (!parse_value(ctx, &s, prop, 1)) {
375                 ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_VALUE,
376                                "HERE-->%s", start);
377                 goto err;
378             }
379         } else {
380             /* A name alone means a true Boolean */
381             prop->type = OSSL_PROPERTY_TYPE_STRING;
382             prop->v.str_val = OSSL_PROPERTY_TRUE;
383         }
384 
385         if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
386             goto err;
387         prop = NULL;
388         done = !match_ch(&s, ',');
389     }
390     if (*s != '\0') {
391         ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
392                        "HERE-->%s", s);
393         goto err;
394     }
395     res = stack_to_property_list(ctx, sk);
396 
397 err:
398     OPENSSL_free(prop);
399     sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
400     return res;
401 }
402 
403 OSSL_PROPERTY_LIST *ossl_parse_query(OSSL_LIB_CTX *ctx, const char *s,
404                                      int create_values)
405 {
406     STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
407     OSSL_PROPERTY_LIST *res = NULL;
408     OSSL_PROPERTY_DEFINITION *prop = NULL;
409     int done;
410 
411     if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
412         return NULL;
413 
414     s = skip_space(s);
415     done = *s == '\0';
416     while (!done) {
417         prop = OPENSSL_malloc(sizeof(*prop));
418         if (prop == NULL)
419             goto err;
420         memset(&prop->v, 0, sizeof(prop->v));
421 
422         if (match_ch(&s, '-')) {
423             prop->oper = OSSL_PROPERTY_OVERRIDE;
424             prop->optional = 0;
425             if (!parse_name(ctx, &s, 1, &prop->name_idx))
426                 goto err;
427             goto skip_value;
428         }
429         prop->optional = match_ch(&s, '?');
430         if (!parse_name(ctx, &s, 1, &prop->name_idx))
431             goto err;
432 
433         if (match_ch(&s, '=')) {
434             prop->oper = OSSL_PROPERTY_OPER_EQ;
435         } else if (MATCH(&s, "!=")) {
436             prop->oper = OSSL_PROPERTY_OPER_NE;
437         } else {
438             /* A name alone is a Boolean comparison for true */
439             prop->oper = OSSL_PROPERTY_OPER_EQ;
440             prop->type = OSSL_PROPERTY_TYPE_STRING;
441             prop->v.str_val = OSSL_PROPERTY_TRUE;
442             goto skip_value;
443         }
444         if (!parse_value(ctx, &s, prop, create_values))
445             prop->type = OSSL_PROPERTY_TYPE_VALUE_UNDEFINED;
446 
447 skip_value:
448         if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
449             goto err;
450         prop = NULL;
451         done = !match_ch(&s, ',');
452     }
453     if (*s != '\0') {
454         ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
455                        "HERE-->%s", s);
456         goto err;
457     }
458     res = stack_to_property_list(ctx, sk);
459 
460 err:
461     OPENSSL_free(prop);
462     sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
463     return res;
464 }
465 
466 /*
467  * Compare a query against a definition.
468  * Return the number of clauses matched or -1 if a mandatory clause is false.
469  */
470 int ossl_property_match_count(const OSSL_PROPERTY_LIST *query,
471                               const OSSL_PROPERTY_LIST *defn)
472 {
473     const OSSL_PROPERTY_DEFINITION *const q = query->properties;
474     const OSSL_PROPERTY_DEFINITION *const d = defn->properties;
475     int i = 0, j = 0, matches = 0;
476     OSSL_PROPERTY_OPER oper;
477 
478     while (i < query->num_properties) {
479         if ((oper = q[i].oper) == OSSL_PROPERTY_OVERRIDE) {
480             i++;
481             continue;
482         }
483         if (j < defn->num_properties) {
484             if (q[i].name_idx > d[j].name_idx) {  /* skip defn, not in query */
485                 j++;
486                 continue;
487             }
488             if (q[i].name_idx == d[j].name_idx) { /* both in defn and query */
489                 const int eq = q[i].type == d[j].type
490                                && memcmp(&q[i].v, &d[j].v, sizeof(q[i].v)) == 0;
491 
492                 if ((eq && oper == OSSL_PROPERTY_OPER_EQ)
493                     || (!eq && oper == OSSL_PROPERTY_OPER_NE))
494                     matches++;
495                 else if (!q[i].optional)
496                     return -1;
497                 i++;
498                 j++;
499                 continue;
500             }
501         }
502 
503         /*
504          * Handle the cases of a missing value and a query with no corresponding
505          * definition.  The former fails for any comparison except inequality,
506          * the latter is treated as a comparison against the Boolean false.
507          */
508         if (q[i].type == OSSL_PROPERTY_TYPE_VALUE_UNDEFINED) {
509             if (oper == OSSL_PROPERTY_OPER_NE)
510                 matches++;
511             else if (!q[i].optional)
512                 return -1;
513         } else if (q[i].type != OSSL_PROPERTY_TYPE_STRING
514                    || (oper == OSSL_PROPERTY_OPER_EQ
515                        && q[i].v.str_val != OSSL_PROPERTY_FALSE)
516                    || (oper == OSSL_PROPERTY_OPER_NE
517                        && q[i].v.str_val == OSSL_PROPERTY_FALSE)) {
518             if (!q[i].optional)
519                 return -1;
520         } else {
521             matches++;
522         }
523         i++;
524     }
525     return matches;
526 }
527 
528 void ossl_property_free(OSSL_PROPERTY_LIST *p)
529 {
530     OPENSSL_free(p);
531 }
532 
533 /*
534  * Merge two property lists.
535  * If there is a common name, the one from the first list is used.
536  */
537 OSSL_PROPERTY_LIST *ossl_property_merge(const OSSL_PROPERTY_LIST *a,
538                                         const OSSL_PROPERTY_LIST *b)
539 {
540     const OSSL_PROPERTY_DEFINITION *const ap = a->properties;
541     const OSSL_PROPERTY_DEFINITION *const bp = b->properties;
542     const OSSL_PROPERTY_DEFINITION *copy;
543     OSSL_PROPERTY_LIST *r;
544     int i, j, n;
545     const int t = a->num_properties + b->num_properties;
546 
547     r = OPENSSL_malloc(sizeof(*r)
548                        + (t == 0 ? 0 : t - 1) * sizeof(r->properties[0]));
549     if (r == NULL)
550         return NULL;
551 
552     r->has_optional = 0;
553     for (i = j = n = 0; i < a->num_properties || j < b->num_properties; n++) {
554         if (i >= a->num_properties) {
555             copy = &bp[j++];
556         } else if (j >= b->num_properties) {
557             copy = &ap[i++];
558         } else if (ap[i].name_idx <= bp[j].name_idx) {
559             if (ap[i].name_idx == bp[j].name_idx)
560                 j++;
561             copy = &ap[i++];
562         } else {
563             copy = &bp[j++];
564         }
565         memcpy(r->properties + n, copy, sizeof(r->properties[0]));
566         r->has_optional |= copy->optional;
567     }
568     r->num_properties = n;
569     if (n != t)
570         r = OPENSSL_realloc(r, sizeof(*r) + (n - 1) * sizeof(r->properties[0]));
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 == 0) {
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     *needed += len;
683 
684     if (*remain == 0)
685         return;
686 
687     BIO_snprintf(*buf, *remain, "%lld", (long long int)val);
688     if (*remain < len) {
689         *buf += *remain;
690         *remain = 0;
691     } else {
692         *buf += len;
693         *remain -= len;
694     }
695 }
696 
697 size_t ossl_property_list_to_string(OSSL_LIB_CTX *ctx,
698                                     const OSSL_PROPERTY_LIST *list, char *buf,
699                                     size_t bufsize)
700 {
701     int i;
702     const OSSL_PROPERTY_DEFINITION *prop = NULL;
703     size_t needed = 0;
704     const char *val;
705 
706     if (list == NULL) {
707         if (bufsize > 0)
708             *buf = '\0';
709         return 1;
710     }
711     if (list->num_properties != 0)
712         prop = &list->properties[list->num_properties - 1];
713     for (i = 0; i < list->num_properties; i++, prop--) {
714         /* Skip invalid names */
715         if (prop->name_idx == 0)
716             continue;
717 
718         if (needed > 0)
719             put_char(',', &buf, &bufsize, &needed);
720 
721         if (prop->optional)
722             put_char('?', &buf, &bufsize, &needed);
723         else if (prop->oper == OSSL_PROPERTY_OVERRIDE)
724             put_char('-', &buf, &bufsize, &needed);
725 
726         val = ossl_property_name_str(ctx, prop->name_idx);
727         if (val == NULL)
728             return 0;
729         put_str(val, &buf, &bufsize, &needed);
730 
731         switch (prop->oper) {
732             case OSSL_PROPERTY_OPER_NE:
733                 put_char('!', &buf, &bufsize, &needed);
734                 /* fall through */
735             case OSSL_PROPERTY_OPER_EQ:
736                 put_char('=', &buf, &bufsize, &needed);
737                 /* put value */
738                 switch (prop->type) {
739                 case OSSL_PROPERTY_TYPE_STRING:
740                     val = ossl_property_value_str(ctx, prop->v.str_val);
741                     if (val == NULL)
742                         return 0;
743                     put_str(val, &buf, &bufsize, &needed);
744                     break;
745 
746                 case OSSL_PROPERTY_TYPE_NUMBER:
747                     put_num(prop->v.int_val, &buf, &bufsize, &needed);
748                     break;
749 
750                 default:
751                     return 0;
752                 }
753                 break;
754             default:
755                 /* do nothing */
756                 break;
757         }
758     }
759 
760     put_char('\0', &buf, &bufsize, &needed);
761     return needed;
762 }
763