test_json.c (18500B)
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 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 TALER_JSON_spec_amount ("amount", 39 "EUR", 40 &a2), 41 GNUNET_JSON_spec_end () 42 }; 43 44 GNUNET_assert (GNUNET_OK == 45 TALER_string_to_amount ("EUR:4.3", 46 &a1)); 47 j = json_pack ("{s:o}", "amount", TALER_JSON_from_amount (&a1)); 48 GNUNET_assert (NULL != j); 49 GNUNET_assert (GNUNET_OK == 50 GNUNET_JSON_parse (j, spec, 51 NULL, NULL)); 52 GNUNET_assert (0 == 53 TALER_amount_cmp (&a1, 54 &a2)); 55 json_decref (j); 56 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 test_amount_array (void) 67 { 68 struct TALER_Amount amounts[2]; 69 struct TALER_Amount *parsed = NULL; 70 size_t parsed_len = 0; 71 struct GNUNET_JSON_Specification spec[2]; 72 json_t *doc; 73 const size_t num_amounts = sizeof (amounts) / sizeof (amounts[0]); 74 75 GNUNET_assert (GNUNET_OK == 76 TALER_string_to_amount ("EUR:1.2", 77 &amounts[0])); 78 GNUNET_assert (GNUNET_OK == 79 TALER_string_to_amount ("EUR:3.4", 80 &amounts[1])); 81 82 spec[0] = TALER_JSON_spec_amount_any_array ("amounts", 83 &parsed_len, 84 &parsed); 85 spec[1] = GNUNET_JSON_spec_end (); 86 87 doc = GNUNET_JSON_PACK ( 88 TALER_JSON_pack_amount_array ("amounts", 89 num_amounts, 90 amounts)); 91 GNUNET_assert (NULL != doc); 92 GNUNET_assert (GNUNET_OK == 93 GNUNET_JSON_parse (doc, 94 spec, 95 NULL, 96 NULL)); 97 GNUNET_assert (parsed_len == num_amounts); 98 for (size_t i = 0; i<num_amounts; i++) 99 GNUNET_assert (0 == 100 TALER_amount_cmp (&amounts[i], 101 &parsed[i])); 102 GNUNET_JSON_parse_free (spec); 103 json_decref (doc); 104 105 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 test_amount_list (void) 116 { 117 struct TALER_AmountList al; 118 struct TALER_AmountList parsed; 119 struct GNUNET_JSON_Specification spec[] = { 120 TALER_JSON_spec_amount_list ("costs", 121 &parsed), 122 GNUNET_JSON_spec_end () 123 }; 124 json_t *doc; 125 126 GNUNET_assert (GNUNET_OK == 127 TALER_string_to_amount_list ("EUR:1.2;CHF:3.4", 128 &al)); 129 doc = GNUNET_JSON_PACK ( 130 TALER_JSON_pack_amount_list ("costs", 131 &al)); 132 GNUNET_assert (NULL != doc); 133 GNUNET_assert (GNUNET_OK == 134 GNUNET_JSON_parse (doc, 135 spec, 136 NULL, 137 NULL)); 138 GNUNET_assert (parsed.tal_len == al.tal_len); 139 for (unsigned int i = 0; i<al.tal_len; i++) 140 GNUNET_assert (0 == 141 TALER_amount_cmp (&al.tal[i], 142 &parsed.tal[i])); 143 GNUNET_JSON_parse_free (spec); 144 json_decref (doc); 145 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 GNUNET_assert (GNUNET_OK == 150 TALER_string_to_amount_list ("", 151 &al)); 152 doc = GNUNET_JSON_PACK ( 153 TALER_JSON_pack_amount_list ("costs", 154 &al)); 155 GNUNET_assert (NULL != doc); 156 GNUNET_assert (json_is_array (json_object_get (doc, 157 "costs"))); 158 GNUNET_assert (GNUNET_OK == 159 GNUNET_JSON_parse (doc, 160 spec, 161 NULL, 162 NULL)); 163 GNUNET_assert (0 == parsed.tal_len); 164 GNUNET_JSON_parse_free (spec); 165 json_decref (doc); 166 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 doc = json_pack ("{s:[s,s,s]}", 172 "costs", 173 "EUR:1", 174 "CHF:2", 175 "EUR:99"); 176 GNUNET_assert (NULL != doc); 177 GNUNET_assert (GNUNET_OK != 178 GNUNET_JSON_parse (doc, 179 spec, 180 NULL, 181 NULL)); 182 json_decref (doc); 183 184 /* as must a malformed entry */ 185 doc = json_pack ("{s:[s,s]}", 186 "costs", 187 "EUR:1", 188 "not-an-amount"); 189 GNUNET_assert (NULL != doc); 190 GNUNET_assert (GNUNET_OK != 191 GNUNET_JSON_parse (doc, 192 spec, 193 NULL, 194 NULL)); 195 json_decref (doc); 196 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 path_cb (void *cls, 214 const char *object_id, 215 json_t *parent) 216 { 217 struct TestPath_Closure *cmp = cls; 218 unsigned int i; 219 220 if (NULL == cmp) 221 return; 222 i = cmp->results_length; 223 if ((0 != strcmp (cmp->object_ids[i], 224 object_id)) || 225 (1 != json_equal (cmp->parents[i], 226 parent))) 227 cmp->cmp_result = 1; 228 cmp->results_length += 1; 229 } 230 231 232 static int 233 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 c1 = json_pack ("{s:s, s:{s:s, s:{s:b}}}", 243 "k1", "v1", 244 "k2", "n1", "n2", 245 /***/ "$forgettable", "n1", true); 246 GNUNET_assert (GNUNET_OK == 247 TALER_JSON_contract_seed_forgettable (c1, 248 c1)); 249 GNUNET_assert (GNUNET_OK == 250 TALER_JSON_contract_hash (c1, 251 &h1)); 252 json_decref (c1); 253 254 c1 = json_pack ("{s:s, s:{s:s, s:{s:s}}}", 255 "k1", "v1", 256 "k2", "n1", "n2", 257 /***/ "$forgettable", "n1", "salt"); 258 GNUNET_assert (NULL != c1); 259 GNUNET_assert (GNUNET_OK == 260 TALER_JSON_contract_mark_forgettable (c1, 261 "k1")); 262 GNUNET_assert (GNUNET_OK == 263 TALER_JSON_contract_mark_forgettable (c1, 264 "k2")); 265 GNUNET_assert (GNUNET_OK == 266 TALER_JSON_contract_hash (c1, 267 &h1)); 268 GNUNET_assert (GNUNET_OK == 269 TALER_JSON_contract_part_forget (c1, 270 "k1")); 271 /* check salt was forgotten */ 272 GNUNET_assert (NULL == 273 json_object_get (json_object_get (c1, 274 "$forgettable"), 275 "k1")); 276 GNUNET_assert (GNUNET_OK == 277 TALER_JSON_contract_hash (c1, 278 &h2)); 279 if (0 != 280 GNUNET_memcmp (&h1, 281 &h2)) 282 { 283 GNUNET_break (0); 284 json_decref (c1); 285 return 1; 286 } 287 GNUNET_assert (GNUNET_OK == 288 TALER_JSON_contract_part_forget (json_object_get (c1, 289 "k2"), 290 "n1")); 291 GNUNET_assert (GNUNET_OK == 292 TALER_JSON_contract_hash (c1, 293 &h2)); 294 if (0 != 295 GNUNET_memcmp (&h1, 296 &h2)) 297 { 298 GNUNET_break (0); 299 json_decref (c1); 300 return 1; 301 } 302 GNUNET_assert (GNUNET_OK == 303 TALER_JSON_contract_part_forget (c1, 304 "k2")); 305 // json_dumpf (c1, stderr, JSON_INDENT (2)); 306 GNUNET_assert (GNUNET_OK == 307 TALER_JSON_contract_hash (c1, 308 &h2)); 309 json_decref (c1); 310 if (0 != 311 GNUNET_memcmp (&h1, 312 &h2)) 313 { 314 GNUNET_break (0); 315 return 1; 316 } 317 318 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 GNUNET_assert (GNUNET_OK == 325 TALER_JSON_contract_hash (c1, 326 &h1)); 327 // json_dumpf (c1, stderr, JSON_INDENT (2)); 328 json_decref (c1); 329 { 330 char *s; 331 332 s = GNUNET_STRINGS_data_to_string_alloc (&h1, 333 sizeof (h1)); 334 if (0 != 335 strcmp (s, 336 "VDE8JPX0AEEE3EX1K8E11RYEWSZQKGGZCV6BWTE4ST1C8711P7H850Z7F2Q2HSSYETX87ERC2JNHWB7GTDWTDWMM716VKPSRBXD7SRR")) 337 { 338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 339 "Invalid reference hash: %s\n", 340 s); 341 GNUNET_free (s); 342 return 1; 343 } 344 GNUNET_free (s); 345 } 346 347 348 c2 = json_pack ("{s:s}", 349 "n1", "n2"); 350 GNUNET_assert (NULL != c2); 351 GNUNET_assert (GNUNET_OK == 352 TALER_JSON_contract_mark_forgettable (c2, 353 "n1")); 354 c3 = json_pack ("{s:s, s:o}", 355 "k1", "v1", 356 "k2", c2); 357 GNUNET_assert (NULL != c3); 358 GNUNET_assert (GNUNET_OK == 359 TALER_JSON_contract_mark_forgettable (c3, 360 "k1")); 361 GNUNET_assert (GNUNET_OK == 362 TALER_JSON_contract_hash (c3, 363 &h1)); 364 GNUNET_assert (GNUNET_OK == 365 TALER_JSON_contract_part_forget (c2, 366 "n1")); 367 GNUNET_assert (GNUNET_OK == 368 TALER_JSON_contract_hash (c3, 369 &h2)); 370 json_decref (c3); 371 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 GNUNET_assert (NULL != c4); 379 GNUNET_assert (GNUNET_SYSERR == 380 TALER_JSON_expand_path (c4, 381 "%.xyz", 382 &path_cb, 383 NULL)); 384 GNUNET_assert (GNUNET_OK == 385 TALER_JSON_expand_path (c4, 386 "$.nonexistent_id", 387 &path_cb, 388 NULL)); 389 GNUNET_assert (GNUNET_SYSERR == 390 TALER_JSON_expand_path (c4, 391 "$.fruit[n]", 392 &path_cb, 393 NULL)); 394 395 { 396 const char *object_ids[] = { "xyz" }; 397 const json_t *parents[] = { 398 json_object_get (c4, 399 "abc1") 400 }; 401 struct TestPath_Closure tp = { 402 .object_ids = object_ids, 403 .parents = parents, 404 .results_length = 0, 405 .cmp_result = 0 406 }; 407 GNUNET_assert (GNUNET_OK == 408 TALER_JSON_expand_path (c4, 409 "$.abc1.xyz", 410 &path_cb, 411 &tp)); 412 GNUNET_assert (1 == tp.results_length); 413 GNUNET_assert (0 == tp.cmp_result); 414 } 415 { 416 const char *object_ids[] = { "name" }; 417 const json_t *parents[] = { 418 json_array_get (json_object_get (c4, 419 "fruit"), 420 0) 421 }; 422 struct TestPath_Closure tp = { 423 .object_ids = object_ids, 424 .parents = parents, 425 .results_length = 0, 426 .cmp_result = 0 427 }; 428 GNUNET_assert (GNUNET_OK == 429 TALER_JSON_expand_path (c4, 430 "$.fruit[0].name", 431 &path_cb, 432 &tp)); 433 GNUNET_assert (1 == tp.results_length); 434 GNUNET_assert (0 == tp.cmp_result); 435 } 436 { 437 const char *object_ids[] = { "name", "name", "name" }; 438 const json_t *parents[] = { 439 json_array_get (json_object_get (c4, 440 "fruit"), 441 0), 442 json_array_get (json_object_get (c4, 443 "fruit"), 444 1), 445 json_array_get (json_object_get (c4, 446 "fruit"), 447 2) 448 }; 449 struct TestPath_Closure tp = { 450 .object_ids = object_ids, 451 .parents = parents, 452 .results_length = 0, 453 .cmp_result = 0 454 }; 455 GNUNET_assert (GNUNET_OK == 456 TALER_JSON_expand_path (c4, 457 "$.fruit[*].name", 458 &path_cb, 459 &tp)); 460 GNUNET_assert (3 == tp.results_length); 461 GNUNET_assert (0 == tp.cmp_result); 462 } 463 json_decref (c4); 464 if (0 != 465 GNUNET_memcmp (&h1, 466 &h2)) 467 { 468 GNUNET_break (0); 469 return 1; 470 } 471 return 0; 472 } 473 474 475 static int 476 test_json_canon (void) 477 { 478 { 479 json_t *c1; 480 char *canon; 481 c1 = json_pack ("{s:s}", 482 "k1", "Hello\nWorld"); 483 484 canon = TALER_JSON_canonicalize (c1); 485 GNUNET_assert (NULL != canon); 486 487 printf ("canon: '%s'\n", canon); 488 489 GNUNET_assert (0 == strcmp (canon, 490 "{\"k1\":\"Hello\\nWorld\"}")); 491 } 492 { 493 json_t *c1; 494 char *canon; 495 c1 = json_pack ("{s:s}", 496 "k1", "Testing “unicode” characters"); 497 498 canon = TALER_JSON_canonicalize (c1); 499 GNUNET_assert (NULL != canon); 500 501 printf ("canon: '%s'\n", canon); 502 503 GNUNET_assert (0 == strcmp (canon, 504 "{\"k1\":\"Testing “unicode” characters\"}")); 505 } 506 { 507 json_t *c1; 508 char *canon; 509 c1 = json_pack ("{s:s}", 510 "k1", "low range \x05 chars"); 511 512 canon = TALER_JSON_canonicalize (c1); 513 GNUNET_assert (NULL != canon); 514 515 printf ("canon: '%s'\n", canon); 516 517 GNUNET_assert (0 == strcmp (canon, 518 "{\"k1\":\"low range \\u0005 chars\"}")); 519 } 520 521 522 return 0; 523 } 524 525 526 static int 527 test_rfc8785 (void) 528 { 529 struct TALER_PrivateContractHashP h1; 530 json_t *c1; 531 532 c1 = json_pack ("{s:s}", 533 "k1", "\x08\x0B\t\1\\\x0d"); 534 GNUNET_assert (GNUNET_OK == 535 TALER_JSON_contract_hash (c1, 536 &h1)); 537 { 538 char *s; 539 540 s = GNUNET_STRINGS_data_to_string_alloc (&h1, 541 sizeof (h1)); 542 if (0 != 543 strcmp (s, 544 "531S33T8ZRGW6548G7T67PMDNGS4Z1D8A2GMB87G3PNKYTW6KGF7Q99XVCGXBKVA2HX6PR5ENJ1PQ5ZTYMMXQB6RM7S82VP7ZG2X5G8")) 545 { 546 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 547 "Invalid reference hash: %s\n", 548 s); 549 GNUNET_free (s); 550 json_decref (c1); 551 return 1; 552 } 553 GNUNET_free (s); 554 } 555 json_decref (c1); 556 return 0; 557 } 558 559 560 static int 561 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 TALER_JSON_spec_array_of_data ("nums", 571 sizeof(*data), 572 &num_data, 573 (void **) &data), 574 GNUNET_JSON_spec_end () 575 }; 576 json_t *d; 577 const char *buf[] = { 578 "01", "02", "03", "04", 579 "Aa", "Bb", "Cc", "Dd" 580 }; 581 582 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 GNUNET_assert (NULL != d); 587 printf ("sizeof(*data)=%ld\n", sizeof(*data)); 588 printf ("array:>>%s<<\n", json_dumps (d, JSON_INDENT (2))); 589 GNUNET_assert (GNUNET_OK == 590 GNUNET_JSON_parse (d, spec, 591 NULL, NULL)); 592 GNUNET_assert (sizeof(buf) / sizeof(*buf) == num_data); 593 for (uint8_t i = 0; i<num_data; i++) 594 { 595 printf ("buf[%d]=%s vs data[%d]=%c%c\n", 596 i, buf[i], 597 i, data[i].chars[0], data[i].chars[1]); 598 if (0 != memcmp (buf[i],&data[i], sizeof(*data))) 599 return 2; 600 } 601 return 0; 602 } 603 604 605 int 606 main (int argc, 607 const char *const argv[]) 608 { 609 (void) argc; 610 (void) argv; 611 GNUNET_log_setup ("test-json", 612 "WARNING", 613 NULL); 614 if (0 != test_amount ()) 615 return 1; 616 if (0 != test_amount_array ()) 617 return 1; 618 if (0 != test_amount_list ()) 619 return 1; 620 if (0 != test_contract ()) 621 return 2; 622 if (0 != test_json_canon ()) 623 return 2; 624 if (0 != test_rfc8785 ()) 625 return 2; 626 if (0 != test_array ()) 627 return 2; 628 return 0; 629 } 630 631 632 /* end of test_json.c */