ac1a8f71c042a91d97858ee7fcbcd3a33dcfb20e
[openssl.git] / test / property_test.c
1 /*
2  * Copyright 2019 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 <stdarg.h>
12 #include "testutil.h"
13 #include "internal/nelem.h"
14 #include "internal/property.h"
15 #include "../crypto/property/property_lcl.h"
16
17 static int add_property_names(const char *n, ...)
18 {
19     va_list args;
20     int res = 1;
21
22     va_start(args, n);
23     do {
24         if (!TEST_int_ne(ossl_property_name(n, 1), 0))
25             res = 0;
26     } while ((n = va_arg(args, const char *)) != NULL);
27     va_end(args);
28     return res;
29 }
30
31 static int test_property_string(void)
32 {
33     OSSL_METHOD_STORE *store;
34     int res = 0;
35     OSSL_PROPERTY_IDX i, j;
36
37     if (TEST_ptr(store = ossl_method_store_new())
38         && TEST_int_eq(ossl_property_name("fnord", 0), 0)
39         && TEST_int_ne(ossl_property_name("fnord", 1), 0)
40         && TEST_int_ne(ossl_property_name("name", 1), 0)
41         /* Property value checks */
42         && TEST_int_eq(ossl_property_value("fnord", 0), 0)
43         && TEST_int_ne(i = ossl_property_value("no", 0), 0)
44         && TEST_int_ne(j = ossl_property_value("yes", 0), 0)
45         && TEST_int_ne(i, j)
46         && TEST_int_eq(ossl_property_value("yes", 1), j)
47         && TEST_int_eq(ossl_property_value("no", 1), i)
48         && TEST_int_ne(i = ossl_property_value("green", 1), 0)
49         && TEST_int_eq(j = ossl_property_value("fnord", 1), i + 1)
50         && TEST_int_eq(ossl_property_value("fnord", 1), j)
51         /* Check name and values are distinct */
52         && TEST_int_eq(ossl_property_value("cold", 0), 0)
53         && TEST_int_ne(ossl_property_name("fnord", 0),
54                        ossl_property_value("fnord", 0)))
55         res = 1;
56     ossl_method_store_free(store);
57     return res;
58 }
59
60 static const struct {
61     const char *defn;
62     const char *query;
63     int e;
64 } parser_tests[] = {
65     { "", "sky=blue", 0 },
66     { "", "sky!=blue", 1 },
67     { "groan", "", 1 },
68     { "cold=yes", "cold=yes", 1 },
69     { "cold=yes", "cold", 1 },
70     { "cold=yes", "cold!=no", 1 },
71     { "groan", "groan=yes", 1 },
72     { "groan", "groan=no", 0 },
73     { "groan", "groan!=yes", 0 },
74     { "cold=no", "cold", 0 },
75     { "cold=no", "cold=no", 1 },
76     { "groan", "cold", 0 },
77     { "groan", "cold=no", 1 },
78     { "groan", "cold!=yes", 1 },
79     { "groan=blue", "groan=yellow", 0 },
80     { "groan=blue", "groan!=yellow", 1 },
81     { "today=monday, tomorrow=3", "today!=2", 1 },
82     { "today=monday, tomorrow=3", "today!='monday'", 0 },
83     { "today=monday, tomorrow=3", "tomorrow=3", 1 },
84     { "n=0x3", "n=3", 1 },
85     { "n=0x3", "n=-3", 0 },
86     { "n=0x33", "n=51", 1 },
87     { "n=033", "n=27", 1 },
88     { "n=0", "n=00", 1 },
89     { "n=0x0", "n=0", 1 },
90 };
91
92 static int test_property_parse(int n)
93 {
94     OSSL_METHOD_STORE *store;
95     OSSL_PROPERTY_LIST *p = NULL, *q = NULL;
96     int r = 0;
97
98     if (TEST_ptr(store = ossl_method_store_new())
99         && add_property_names("sky", "groan", "cold", "today", "tomorrow", "n",
100                               NULL)
101         && TEST_ptr(p = ossl_parse_property(parser_tests[n].defn))
102         && TEST_ptr(q = ossl_parse_query(parser_tests[n].query))
103         && TEST_int_eq(ossl_property_match(q, p), parser_tests[n].e))
104         r = 1;
105     ossl_property_free(p);
106     ossl_property_free(q);
107     ossl_method_store_free(store);
108     return r;
109 }
110
111 static const struct {
112     const char *q_global;
113     const char *q_local;
114     const char *prop;
115 } merge_tests[] = {
116     { "", "colour=blue", "colour=blue" },
117     { "colour=blue", "", "colour=blue" },
118     { "colour=red", "colour=blue", "colour=blue" },
119     { "clouds=pink, urn=red", "urn=blue, colour=green",
120         "urn=blue, colour=green, clouds=pink" },
121     { "pot=gold", "urn=blue", "pot=gold, urn=blue" },
122     { "night", "day", "day=yes, night=yes" },
123     { "day", "night", "day=yes, night=yes" },
124     { "", "", "" },
125     /*
126      * The following four leave 'day' unspecified in the query, and will match
127      * any definition
128      */
129     { "day=yes", "-day", "day=no" },
130     { "day=yes", "-day", "day=yes" },
131     { "day=yes", "-day", "day=arglebargle" },
132     { "day=yes", "-day", "pot=sesquioxidizing" },
133     { "day, night", "-night, day", "day=yes, night=no" },
134     { "-day", "day=yes", "day=yes" },
135 };
136
137 static int test_property_merge(int n)
138 {
139     OSSL_METHOD_STORE *store;
140     OSSL_PROPERTY_LIST *q_global = NULL, *q_local = NULL;
141     OSSL_PROPERTY_LIST *q_combined = NULL, *prop = NULL;
142     int r = 0;
143
144     if (TEST_ptr(store = ossl_method_store_new())
145         && add_property_names("colour", "urn", "clouds", "pot", "day", "night",
146                               NULL)
147         && TEST_ptr(prop = ossl_parse_property(merge_tests[n].prop))
148         && TEST_ptr(q_global = ossl_parse_query(merge_tests[n].q_global))
149         && TEST_ptr(q_local = ossl_parse_query(merge_tests[n].q_local))
150         && TEST_ptr(q_combined = ossl_property_merge(q_local, q_global))
151         && TEST_true(ossl_property_match(q_combined, prop)))
152         r = 1;
153     ossl_property_free(q_global);
154     ossl_property_free(q_local);
155     ossl_property_free(q_combined);
156     ossl_property_free(prop);
157     ossl_method_store_free(store);
158     return r;
159 }
160
161 static int test_property_defn_cache(void)
162 {
163     OSSL_METHOD_STORE *store;
164     OSSL_PROPERTY_LIST *red, *blue;
165     int r = 0;
166
167     if (TEST_ptr(store = ossl_method_store_new())
168         && add_property_names("red", "blue", NULL)
169         && TEST_ptr(red = ossl_parse_property("red"))
170         && TEST_ptr(blue = ossl_parse_property("blue"))
171         && TEST_ptr_ne(red, blue)
172         && TEST_true(ossl_prop_defn_set("red", red))
173         && TEST_true(ossl_prop_defn_set("blue", blue))
174         && TEST_ptr_eq(ossl_prop_defn_get("red"), red)
175         && TEST_ptr_eq(ossl_prop_defn_get("blue"), blue))
176         r = 1;
177     ossl_method_store_free(store);
178     return r;
179 }
180
181 static const struct {
182     const char *defn;
183     const char *query;
184     int e;
185 } definition_tests[] = {
186     { "alpha", "alpha=yes", 1 },
187     { "alpha=no", "alpha", 0 },
188     { "alpha=1", "alpha=1", 1 },
189     { "alpha=2", "alpha=1", 0 },
190     { "alpha", "omega", 0 }
191 };
192
193 static int test_definition_compares(int n)
194 {
195     OSSL_METHOD_STORE *store;
196     OSSL_PROPERTY_LIST *d = NULL, *q = NULL;
197     int r;
198
199     r = TEST_ptr(store = ossl_method_store_new())
200         && add_property_names("alpha", "omega", NULL)
201         && TEST_ptr(d = ossl_parse_property(definition_tests[n].defn))
202         && TEST_ptr(q = ossl_parse_query(definition_tests[n].query))
203         && TEST_int_eq(ossl_property_match(q, d), definition_tests[n].e);
204
205     ossl_property_free(d);
206     ossl_property_free(q);
207     ossl_method_store_free(store);
208     return r;
209 }
210
211 static int test_register_deregister(void)
212 {
213     static const struct {
214         int nid;
215         const char *prop;
216         char *impl;
217     } impls[] = {
218         { 6, "position=1", "a" },
219         { 6, "position=2", "b" },
220         { 6, "position=3", "c" },
221         { 6, "position=4", "d" },
222     };
223     size_t i;
224     int ret = 0;
225     OSSL_METHOD_STORE *store;
226
227     if (!TEST_ptr(store = ossl_method_store_new())
228         || !add_property_names("position", NULL))
229         goto err;
230
231     for (i = 0; i < OSSL_NELEM(impls); i++)
232         if (!TEST_true(ossl_method_store_add(store, impls[i].nid, impls[i].prop,
233                                              impls[i].impl, NULL))) {
234             TEST_note("iteration %zd", i + 1);
235             goto err;
236         }
237
238     /* Deregister in a different order to registration */
239     for (i = 0; i < OSSL_NELEM(impls); i++) {
240         const size_t j = (1 + i * 3) % OSSL_NELEM(impls);
241         int nid = impls[j].nid;
242         void *impl = impls[j].impl;
243
244         if (!TEST_true(ossl_method_store_remove(store, nid, impl))
245             || !TEST_false(ossl_method_store_remove(store, nid, impl))) {
246             TEST_note("iteration %zd, position %zd", i + 1, j + 1);
247             goto err;
248         }
249     }
250
251     if (TEST_false(ossl_method_store_remove(store, impls[0].nid, impls[0].impl)))
252         ret = 1;
253 err:
254     ossl_method_store_free(store);
255     return ret;
256 }
257
258 static int test_property(void)
259 {
260     static const struct {
261         int nid;
262         const char *prop;
263         char *impl;
264     } impls[] = {
265         { 1, "fast=no, colour=green", "a" },
266         { 1, "fast, colour=blue", "b" },
267         { 1, "", "-" },
268         { 9, "sky=blue, furry", "c" },
269         { 3, NULL, "d" },
270         { 6, "sky.colour=blue, sky=green, old.data", "e" },
271     };
272     static struct {
273         int nid;
274         const char *prop;
275         char *expected;
276     } queries[] = {
277         { 1, "fast", "b" },
278         { 1, "fast=yes", "b" },
279         { 1, "fast=no, colour=green", "a" },
280         { 1, "colour=blue, fast", "b" },
281         { 1, "colour=blue", "b" },
282         { 9, "furry", "c" },
283         { 6, "sky.colour=blue", "e" },
284         { 6, "old.data", "e" },
285         { 9, "furry=yes, sky=blue", "c" },
286         { 1, "", "a" },
287         { 3, "", "d" },
288     };
289     OSSL_METHOD_STORE *store;
290     size_t i;
291     int ret = 0;
292     void *result;
293
294     if (!TEST_ptr(store = ossl_method_store_new())
295         || !add_property_names("fast", "colour", "sky", "furry", NULL))
296         goto err;
297
298     for (i = 0; i < OSSL_NELEM(impls); i++)
299         if (!TEST_true(ossl_method_store_add(store, impls[i].nid, impls[i].prop,
300                                              impls[i].impl, NULL))) {
301             TEST_note("iteration %zd", i + 1);
302             goto err;
303         }
304     for (i = 0; i < OSSL_NELEM(queries); i++) {
305         OSSL_PROPERTY_LIST *pq = NULL;
306
307         if (!TEST_true(ossl_property_read_lock(store))
308             || !TEST_true(ossl_method_store_fetch(store, queries[i].nid,
309                                                   queries[i].prop, &result))
310             || !TEST_true(ossl_property_unlock(store))
311             || !TEST_str_eq((char *)result, queries[i].expected)) {
312             TEST_note("iteration %zd", i + 1);
313             ossl_property_free(pq);
314             goto err;
315         }
316         ossl_property_free(pq);
317     }
318     ret = 1;
319 err:
320     ossl_method_store_free(store);
321     return ret;
322 }
323
324 static int test_query_cache_stochastic(void)
325 {
326     const int max = 10000, tail = 10;
327     OSSL_METHOD_STORE *store;
328     int i, res = 0;
329     char buf[50];
330     void *result;
331     int errors = 0;
332     int v[10001];
333
334     if (!TEST_ptr(store = ossl_method_store_new())
335         || !add_property_names("n", NULL))
336         goto err;
337
338     for (i = 1; i <= max; i++) {
339         v[i] = 2 * i;
340         BIO_snprintf(buf, sizeof(buf), "n=%d\n", i);
341         if (!TEST_true(ossl_method_store_add(store, i, buf, "abc", NULL))
342                 || !TEST_true(ossl_method_store_cache_set(store, i, buf, v + i))
343                 || !TEST_true(ossl_method_store_cache_set(store, i, "n=1234",
344                                                           "miss"))) {
345             TEST_note("iteration %d", i);
346             goto err;
347         }
348     }
349     for (i = 1; i <= max; i++) {
350         BIO_snprintf(buf, sizeof(buf), "n=%d\n", i);
351         if (!ossl_method_store_cache_get(store, i, buf, &result)
352             || result != v + i)
353             errors++;
354     }
355     /* There is a tiny probability that this will fail when it shouldn't */
356     res = TEST_int_gt(errors, tail) && TEST_int_lt(errors, max - tail);
357
358 err:
359     ossl_method_store_free(store);
360     return res;
361 }
362
363 int setup_tests(void)
364 {
365     ADD_TEST(test_property_string);
366     ADD_ALL_TESTS(test_property_parse, OSSL_NELEM(parser_tests));
367     ADD_ALL_TESTS(test_property_merge, OSSL_NELEM(merge_tests));
368     ADD_TEST(test_property_defn_cache);
369     ADD_ALL_TESTS(test_definition_compares, OSSL_NELEM(definition_tests));
370     ADD_TEST(test_register_deregister);
371     ADD_TEST(test_property);
372     ADD_TEST(test_query_cache_stochastic);
373     return 1;
374 }