Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2015, 2016, 2020 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 :
17 : /**
18 : * @file json/test_json.c
19 : * @brief Tests for Taler-specific crypto logic
20 : * @author Christian Grothoff <christian@grothoff.org>
21 : */
22 : #include "taler/taler_util.h"
23 : #include "taler/taler_json_lib.h"
24 :
25 :
26 : /**
27 : * Test amount conversion from/to JSON.
28 : *
29 : * @return 0 on success
30 : */
31 : static int
32 1 : test_amount (void)
33 : {
34 : json_t *j;
35 : struct TALER_Amount a1;
36 : struct TALER_Amount a2;
37 : struct GNUNET_JSON_Specification spec[] = {
38 1 : TALER_JSON_spec_amount ("amount",
39 : "EUR",
40 : &a2),
41 1 : GNUNET_JSON_spec_end ()
42 : };
43 :
44 1 : GNUNET_assert (GNUNET_OK ==
45 : TALER_string_to_amount ("EUR:4.3",
46 : &a1));
47 1 : j = json_pack ("{s:o}", "amount", TALER_JSON_from_amount (&a1));
48 1 : GNUNET_assert (NULL != j);
49 1 : GNUNET_assert (GNUNET_OK ==
50 : GNUNET_JSON_parse (j, spec,
51 : NULL, NULL));
52 1 : GNUNET_assert (0 ==
53 : TALER_amount_cmp (&a1,
54 : &a2));
55 1 : json_decref (j);
56 1 : return 0;
57 : }
58 :
59 :
60 : /**
61 : * Verify JSON packing/parsing for amount arrays.
62 : *
63 : * @return 0 on success
64 : */
65 : static int
66 1 : test_amount_array (void)
67 : {
68 : struct TALER_Amount amounts[2];
69 1 : struct TALER_Amount *parsed = NULL;
70 1 : size_t parsed_len = 0;
71 : struct GNUNET_JSON_Specification spec[2];
72 : json_t *doc;
73 1 : const size_t num_amounts = sizeof (amounts) / sizeof (amounts[0]);
74 :
75 1 : GNUNET_assert (GNUNET_OK ==
76 : TALER_string_to_amount ("EUR:1.2",
77 : &amounts[0]));
78 1 : GNUNET_assert (GNUNET_OK ==
79 : TALER_string_to_amount ("EUR:3.4",
80 : &amounts[1]));
81 :
82 1 : spec[0] = TALER_JSON_spec_amount_any_array ("amounts",
83 : &parsed_len,
84 : &parsed);
85 1 : spec[1] = GNUNET_JSON_spec_end ();
86 :
87 1 : doc = GNUNET_JSON_PACK (
88 : TALER_JSON_pack_amount_array ("amounts",
89 : num_amounts,
90 : amounts));
91 1 : GNUNET_assert (NULL != doc);
92 1 : GNUNET_assert (GNUNET_OK ==
93 : GNUNET_JSON_parse (doc,
94 : spec,
95 : NULL,
96 : NULL));
97 1 : GNUNET_assert (parsed_len == num_amounts);
98 3 : for (size_t i = 0; i<num_amounts; i++)
99 2 : GNUNET_assert (0 ==
100 : TALER_amount_cmp (&amounts[i],
101 : &parsed[i]));
102 1 : GNUNET_JSON_parse_free (spec);
103 1 : json_decref (doc);
104 :
105 1 : return 0;
106 : }
107 :
108 :
109 : /**
110 : * Verify JSON packing/parsing for price lists.
111 : *
112 : * @return 0 on success
113 : */
114 : static int
115 1 : test_amount_list (void)
116 : {
117 : struct TALER_AmountList al;
118 : struct TALER_AmountList parsed;
119 : struct GNUNET_JSON_Specification spec[] = {
120 1 : TALER_JSON_spec_amount_list ("costs",
121 : &parsed),
122 1 : GNUNET_JSON_spec_end ()
123 : };
124 : json_t *doc;
125 :
126 1 : GNUNET_assert (GNUNET_OK ==
127 : TALER_string_to_amount_list ("EUR:1.2;CHF:3.4",
128 : &al));
129 1 : doc = GNUNET_JSON_PACK (
130 : TALER_JSON_pack_amount_list ("costs",
131 : &al));
132 1 : GNUNET_assert (NULL != doc);
133 1 : GNUNET_assert (GNUNET_OK ==
134 : GNUNET_JSON_parse (doc,
135 : spec,
136 : NULL,
137 : NULL));
138 1 : GNUNET_assert (parsed.tal_len == al.tal_len);
139 3 : for (unsigned int i = 0; i<al.tal_len; i++)
140 2 : GNUNET_assert (0 ==
141 : TALER_amount_cmp (&al.tal[i],
142 : &parsed.tal[i]));
143 1 : GNUNET_JSON_parse_free (spec);
144 1 : json_decref (doc);
145 1 : TALER_amount_list_free (&al);
146 :
147 : /* an empty list must survive as an empty array, and not
148 : become JSON null: "free" is a meaningful price */
149 1 : GNUNET_assert (GNUNET_OK ==
150 : TALER_string_to_amount_list ("",
151 : &al));
152 1 : doc = GNUNET_JSON_PACK (
153 : TALER_JSON_pack_amount_list ("costs",
154 : &al));
155 1 : GNUNET_assert (NULL != doc);
156 1 : GNUNET_assert (json_is_array (json_object_get (doc,
157 : "costs")));
158 1 : GNUNET_assert (GNUNET_OK ==
159 : GNUNET_JSON_parse (doc,
160 : spec,
161 : NULL,
162 : NULL));
163 1 : GNUNET_assert (0 == parsed.tal_len);
164 1 : GNUNET_JSON_parse_free (spec);
165 1 : json_decref (doc);
166 1 : TALER_amount_list_free (&al);
167 :
168 : /* a repeated currency on the wire must be rejected, or a peer
169 : could advertise two prices and have us pick a different one
170 : than the user was shown */
171 1 : doc = json_pack ("{s:[s,s,s]}",
172 : "costs",
173 : "EUR:1",
174 : "CHF:2",
175 : "EUR:99");
176 1 : GNUNET_assert (NULL != doc);
177 1 : GNUNET_assert (GNUNET_OK !=
178 : GNUNET_JSON_parse (doc,
179 : spec,
180 : NULL,
181 : NULL));
182 1 : json_decref (doc);
183 :
184 : /* as must a malformed entry */
185 1 : doc = json_pack ("{s:[s,s]}",
186 : "costs",
187 : "EUR:1",
188 : "not-an-amount");
189 1 : GNUNET_assert (NULL != doc);
190 1 : GNUNET_assert (GNUNET_OK !=
191 : GNUNET_JSON_parse (doc,
192 : spec,
193 : NULL,
194 : NULL));
195 1 : json_decref (doc);
196 1 : return 0;
197 : }
198 :
199 :
200 : struct TestPath_Closure
201 : {
202 : const char **object_ids;
203 :
204 : const json_t **parents;
205 :
206 : unsigned int results_length;
207 :
208 : int cmp_result;
209 : };
210 :
211 :
212 : static void
213 5 : path_cb (void *cls,
214 : const char *object_id,
215 : json_t *parent)
216 : {
217 5 : struct TestPath_Closure *cmp = cls;
218 : unsigned int i;
219 :
220 5 : if (NULL == cmp)
221 0 : return;
222 5 : i = cmp->results_length;
223 5 : if ((0 != strcmp (cmp->object_ids[i],
224 5 : object_id)) ||
225 5 : (1 != json_equal (cmp->parents[i],
226 : parent)))
227 0 : cmp->cmp_result = 1;
228 5 : cmp->results_length += 1;
229 : }
230 :
231 :
232 : static int
233 1 : test_contract (void)
234 : {
235 : struct TALER_PrivateContractHashP h1;
236 : struct TALER_PrivateContractHashP h2;
237 : json_t *c1;
238 : json_t *c2;
239 : json_t *c3;
240 : json_t *c4;
241 :
242 1 : c1 = json_pack ("{s:s, s:{s:s, s:{s:b}}}",
243 : "k1", "v1",
244 : "k2", "n1", "n2",
245 : /***/ "$forgettable", "n1", true);
246 1 : GNUNET_assert (GNUNET_OK ==
247 : TALER_JSON_contract_seed_forgettable (c1,
248 : c1));
249 1 : GNUNET_assert (GNUNET_OK ==
250 : TALER_JSON_contract_hash (c1,
251 : &h1));
252 1 : json_decref (c1);
253 :
254 1 : c1 = json_pack ("{s:s, s:{s:s, s:{s:s}}}",
255 : "k1", "v1",
256 : "k2", "n1", "n2",
257 : /***/ "$forgettable", "n1", "salt");
258 1 : GNUNET_assert (NULL != c1);
259 1 : GNUNET_assert (GNUNET_OK ==
260 : TALER_JSON_contract_mark_forgettable (c1,
261 : "k1"));
262 1 : GNUNET_assert (GNUNET_OK ==
263 : TALER_JSON_contract_mark_forgettable (c1,
264 : "k2"));
265 1 : GNUNET_assert (GNUNET_OK ==
266 : TALER_JSON_contract_hash (c1,
267 : &h1));
268 1 : GNUNET_assert (GNUNET_OK ==
269 : TALER_JSON_contract_part_forget (c1,
270 : "k1"));
271 : /* check salt was forgotten */
272 1 : GNUNET_assert (NULL ==
273 : json_object_get (json_object_get (c1,
274 : "$forgettable"),
275 : "k1"));
276 1 : GNUNET_assert (GNUNET_OK ==
277 : TALER_JSON_contract_hash (c1,
278 : &h2));
279 1 : if (0 !=
280 1 : GNUNET_memcmp (&h1,
281 : &h2))
282 : {
283 0 : GNUNET_break (0);
284 0 : json_decref (c1);
285 0 : return 1;
286 : }
287 1 : GNUNET_assert (GNUNET_OK ==
288 : TALER_JSON_contract_part_forget (json_object_get (c1,
289 : "k2"),
290 : "n1"));
291 1 : GNUNET_assert (GNUNET_OK ==
292 : TALER_JSON_contract_hash (c1,
293 : &h2));
294 1 : if (0 !=
295 1 : GNUNET_memcmp (&h1,
296 : &h2))
297 : {
298 0 : GNUNET_break (0);
299 0 : json_decref (c1);
300 0 : return 1;
301 : }
302 1 : GNUNET_assert (GNUNET_OK ==
303 : TALER_JSON_contract_part_forget (c1,
304 : "k2"));
305 : // json_dumpf (c1, stderr, JSON_INDENT (2));
306 1 : GNUNET_assert (GNUNET_OK ==
307 : TALER_JSON_contract_hash (c1,
308 : &h2));
309 1 : json_decref (c1);
310 1 : if (0 !=
311 1 : GNUNET_memcmp (&h1,
312 : &h2))
313 : {
314 0 : GNUNET_break (0);
315 0 : return 1;
316 : }
317 :
318 1 : c1 = json_pack ("{s:I, s:{s:s}, s:{s:b, s:{s:s}}, s:{s:s}}",
319 : "k1", 1,
320 : "$forgettable", "k1", "SALT",
321 : "k2", "n1", true,
322 : /***/ "$forgettable", "n1", "salt",
323 : "k3", "n1", "string");
324 1 : GNUNET_assert (GNUNET_OK ==
325 : TALER_JSON_contract_hash (c1,
326 : &h1));
327 : // json_dumpf (c1, stderr, JSON_INDENT (2));
328 1 : json_decref (c1);
329 : {
330 : char *s;
331 :
332 1 : s = GNUNET_STRINGS_data_to_string_alloc (&h1,
333 : sizeof (h1));
334 1 : if (0 !=
335 1 : strcmp (s,
336 : "VDE8JPX0AEEE3EX1K8E11RYEWSZQKGGZCV6BWTE4ST1C8711P7H850Z7F2Q2HSSYETX87ERC2JNHWB7GTDWTDWMM716VKPSRBXD7SRR"))
337 : {
338 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
339 : "Invalid reference hash: %s\n",
340 : s);
341 0 : GNUNET_free (s);
342 0 : return 1;
343 : }
344 1 : GNUNET_free (s);
345 : }
346 :
347 :
348 1 : c2 = json_pack ("{s:s}",
349 : "n1", "n2");
350 1 : GNUNET_assert (NULL != c2);
351 1 : GNUNET_assert (GNUNET_OK ==
352 : TALER_JSON_contract_mark_forgettable (c2,
353 : "n1"));
354 1 : c3 = json_pack ("{s:s, s:o}",
355 : "k1", "v1",
356 : "k2", c2);
357 1 : GNUNET_assert (NULL != c3);
358 1 : GNUNET_assert (GNUNET_OK ==
359 : TALER_JSON_contract_mark_forgettable (c3,
360 : "k1"));
361 1 : GNUNET_assert (GNUNET_OK ==
362 : TALER_JSON_contract_hash (c3,
363 : &h1));
364 1 : GNUNET_assert (GNUNET_OK ==
365 : TALER_JSON_contract_part_forget (c2,
366 : "n1"));
367 1 : GNUNET_assert (GNUNET_OK ==
368 : TALER_JSON_contract_hash (c3,
369 : &h2));
370 1 : json_decref (c3);
371 1 : c4 = json_pack ("{s:{s:s}, s:[{s:s}, {s:s}, {s:s}]}",
372 : "abc1",
373 : "xyz", "value",
374 : "fruit",
375 : "name", "banana",
376 : "name", "apple",
377 : "name", "orange");
378 1 : GNUNET_assert (NULL != c4);
379 1 : GNUNET_assert (GNUNET_SYSERR ==
380 : TALER_JSON_expand_path (c4,
381 : "%.xyz",
382 : &path_cb,
383 : NULL));
384 1 : GNUNET_assert (GNUNET_OK ==
385 : TALER_JSON_expand_path (c4,
386 : "$.nonexistent_id",
387 : &path_cb,
388 : NULL));
389 1 : GNUNET_assert (GNUNET_SYSERR ==
390 : TALER_JSON_expand_path (c4,
391 : "$.fruit[n]",
392 : &path_cb,
393 : NULL));
394 :
395 : {
396 1 : const char *object_ids[] = { "xyz" };
397 1 : const json_t *parents[] = {
398 1 : json_object_get (c4,
399 : "abc1")
400 : };
401 1 : struct TestPath_Closure tp = {
402 : .object_ids = object_ids,
403 : .parents = parents,
404 : .results_length = 0,
405 : .cmp_result = 0
406 : };
407 1 : GNUNET_assert (GNUNET_OK ==
408 : TALER_JSON_expand_path (c4,
409 : "$.abc1.xyz",
410 : &path_cb,
411 : &tp));
412 1 : GNUNET_assert (1 == tp.results_length);
413 1 : GNUNET_assert (0 == tp.cmp_result);
414 : }
415 : {
416 1 : const char *object_ids[] = { "name" };
417 1 : const json_t *parents[] = {
418 1 : json_array_get (json_object_get (c4,
419 : "fruit"),
420 : 0)
421 : };
422 1 : struct TestPath_Closure tp = {
423 : .object_ids = object_ids,
424 : .parents = parents,
425 : .results_length = 0,
426 : .cmp_result = 0
427 : };
428 1 : GNUNET_assert (GNUNET_OK ==
429 : TALER_JSON_expand_path (c4,
430 : "$.fruit[0].name",
431 : &path_cb,
432 : &tp));
433 1 : GNUNET_assert (1 == tp.results_length);
434 1 : GNUNET_assert (0 == tp.cmp_result);
435 : }
436 : {
437 1 : const char *object_ids[] = { "name", "name", "name" };
438 3 : const json_t *parents[] = {
439 1 : json_array_get (json_object_get (c4,
440 : "fruit"),
441 : 0),
442 1 : json_array_get (json_object_get (c4,
443 : "fruit"),
444 : 1),
445 1 : json_array_get (json_object_get (c4,
446 : "fruit"),
447 : 2)
448 : };
449 1 : struct TestPath_Closure tp = {
450 : .object_ids = object_ids,
451 : .parents = parents,
452 : .results_length = 0,
453 : .cmp_result = 0
454 : };
455 1 : GNUNET_assert (GNUNET_OK ==
456 : TALER_JSON_expand_path (c4,
457 : "$.fruit[*].name",
458 : &path_cb,
459 : &tp));
460 1 : GNUNET_assert (3 == tp.results_length);
461 1 : GNUNET_assert (0 == tp.cmp_result);
462 : }
463 1 : json_decref (c4);
464 1 : if (0 !=
465 1 : GNUNET_memcmp (&h1,
466 : &h2))
467 : {
468 0 : GNUNET_break (0);
469 0 : return 1;
470 : }
471 1 : return 0;
472 : }
473 :
474 :
475 : static int
476 1 : test_json_canon (void)
477 : {
478 : {
479 : json_t *c1;
480 : char *canon;
481 1 : c1 = json_pack ("{s:s}",
482 : "k1", "Hello\nWorld");
483 :
484 1 : canon = TALER_JSON_canonicalize (c1);
485 1 : GNUNET_assert (NULL != canon);
486 :
487 1 : printf ("canon: '%s'\n", canon);
488 :
489 1 : GNUNET_assert (0 == strcmp (canon,
490 : "{\"k1\":\"Hello\\nWorld\"}"));
491 : }
492 : {
493 : json_t *c1;
494 : char *canon;
495 1 : c1 = json_pack ("{s:s}",
496 : "k1", "Testing “unicode” characters");
497 :
498 1 : canon = TALER_JSON_canonicalize (c1);
499 1 : GNUNET_assert (NULL != canon);
500 :
501 1 : printf ("canon: '%s'\n", canon);
502 :
503 1 : GNUNET_assert (0 == strcmp (canon,
504 : "{\"k1\":\"Testing “unicode” characters\"}"));
505 : }
506 : {
507 : json_t *c1;
508 : char *canon;
509 1 : c1 = json_pack ("{s:s}",
510 : "k1", "low range \x05 chars");
511 :
512 1 : canon = TALER_JSON_canonicalize (c1);
513 1 : GNUNET_assert (NULL != canon);
514 :
515 1 : printf ("canon: '%s'\n", canon);
516 :
517 1 : GNUNET_assert (0 == strcmp (canon,
518 : "{\"k1\":\"low range \\u0005 chars\"}"));
519 : }
520 :
521 :
522 1 : return 0;
523 : }
524 :
525 :
526 : static int
527 1 : test_rfc8785 (void)
528 : {
529 : struct TALER_PrivateContractHashP h1;
530 : json_t *c1;
531 :
532 1 : c1 = json_pack ("{s:s}",
533 : "k1", "\x08\x0B\t\1\\\x0d");
534 1 : GNUNET_assert (GNUNET_OK ==
535 : TALER_JSON_contract_hash (c1,
536 : &h1));
537 : {
538 : char *s;
539 :
540 1 : s = GNUNET_STRINGS_data_to_string_alloc (&h1,
541 : sizeof (h1));
542 1 : if (0 !=
543 1 : strcmp (s,
544 : "531S33T8ZRGW6548G7T67PMDNGS4Z1D8A2GMB87G3PNKYTW6KGF7Q99XVCGXBKVA2HX6PR5ENJ1PQ5ZTYMMXQB6RM7S82VP7ZG2X5G8"))
545 : {
546 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
547 : "Invalid reference hash: %s\n",
548 : s);
549 0 : GNUNET_free (s);
550 0 : json_decref (c1);
551 0 : return 1;
552 : }
553 1 : GNUNET_free (s);
554 : }
555 1 : json_decref (c1);
556 1 : return 0;
557 : }
558 :
559 :
560 : static int
561 1 : test_array (void)
562 : {
563 : struct _data
564 : {
565 : char chars[2];
566 : };
567 : struct _data *data;
568 : size_t num_data;
569 : struct GNUNET_JSON_Specification spec[] = {
570 1 : TALER_JSON_spec_array_of_data ("nums",
571 : sizeof(*data),
572 : &num_data,
573 : (void **) &data),
574 1 : GNUNET_JSON_spec_end ()
575 : };
576 : json_t *d;
577 1 : const char *buf[] = {
578 : "01", "02", "03", "04",
579 : "Aa", "Bb", "Cc", "Dd"
580 : };
581 :
582 1 : d = json_pack ("{s:[s:s:s:s:s:s:s:s]}",
583 : "nums",
584 : "60RG","60S0","60SG","60T0",
585 : "85GG","89H0","8DHG","8HJ0");
586 1 : GNUNET_assert (NULL != d);
587 1 : printf ("sizeof(*data)=%ld\n", sizeof(*data));
588 1 : printf ("array:>>%s<<\n", json_dumps (d, JSON_INDENT (2)));
589 1 : GNUNET_assert (GNUNET_OK ==
590 : GNUNET_JSON_parse (d, spec,
591 : NULL, NULL));
592 1 : GNUNET_assert (sizeof(buf) / sizeof(*buf) == num_data);
593 9 : for (uint8_t i = 0; i<num_data; i++)
594 : {
595 8 : printf ("buf[%d]=%s vs data[%d]=%c%c\n",
596 : i, buf[i],
597 8 : i, data[i].chars[0], data[i].chars[1]);
598 8 : if (0 != memcmp (buf[i],&data[i], sizeof(*data)))
599 0 : return 2;
600 : }
601 1 : return 0;
602 : }
603 :
604 :
605 : int
606 1 : main (int argc,
607 : const char *const argv[])
608 : {
609 : (void) argc;
610 : (void) argv;
611 1 : GNUNET_log_setup ("test-json",
612 : "WARNING",
613 : NULL);
614 1 : if (0 != test_amount ())
615 0 : return 1;
616 1 : if (0 != test_amount_array ())
617 0 : return 1;
618 1 : if (0 != test_amount_list ())
619 0 : return 1;
620 1 : if (0 != test_contract ())
621 0 : return 2;
622 1 : if (0 != test_json_canon ())
623 0 : return 2;
624 1 : if (0 != test_rfc8785 ())
625 0 : return 2;
626 1 : if (0 != test_array ())
627 0 : return 2;
628 1 : return 0;
629 : }
630 :
631 :
632 : /* end of test_json.c */
|