json.c (18968B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 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 * @file json/json.c 18 * @brief helper functions for JSON processing using libjansson 19 * @author Sree Harsha Totakura <sreeharsha@totakura.in> 20 * @author Christian Grothoff 21 */ 22 #include "donau_config.h" 23 #include "donau_json_lib.h" 24 #include <gnunet/gnunet_util_lib.h> 25 #include <taler/taler_util.h> 26 #include <taler/taler_json_lib.h> 27 #include <unistr.h> 28 29 30 /** 31 * Check if @a json contains a 'real' value anywhere. 32 * 33 * @param json json to check 34 * @return true if a real is in it somewhere 35 */ 36 static bool 37 contains_real (const json_t *json) 38 { 39 if (json_is_real (json)) 40 return true; 41 if (json_is_object (json)) 42 { 43 json_t *member; 44 const char *name; 45 46 json_object_foreach ((json_t *) json, name, member) 47 if (contains_real (member)) 48 return true; 49 return false; 50 } 51 if (json_is_array (json)) 52 { 53 json_t *member; 54 size_t index; 55 56 json_array_foreach ((json_t *) json, index, member) 57 if (contains_real (member)) 58 return true; 59 return false; 60 } 61 return false; 62 } 63 64 65 /** 66 * Dump the @a json to a string and hash it. 67 * 68 * @param json value to hash 69 * @param salt salt value to include when using HKDF, 70 * NULL to not use any salt and to use SHA512 71 * @param[out] hc where to store the hash 72 * @return #GNUNET_OK on success, 73 * #GNUNET_NO if @a json was not hash-able 74 * #GNUNET_SYSERR on failure 75 */ 76 static enum GNUNET_GenericReturnValue 77 dump_and_hash (const json_t *json, 78 const char *salt, 79 struct GNUNET_HashCode *hc) 80 { 81 char *wire_enc; 82 size_t len; 83 84 if (NULL == json) 85 { 86 GNUNET_break_op (0); 87 return GNUNET_NO; 88 } 89 if (contains_real (json)) 90 { 91 GNUNET_break_op (0); 92 return GNUNET_NO; 93 } 94 if (NULL == (wire_enc = json_dumps (json, 95 JSON_ENCODE_ANY 96 | JSON_COMPACT 97 | JSON_SORT_KEYS))) 98 { 99 GNUNET_break (0); 100 return GNUNET_SYSERR; 101 } 102 len = TALER_rfc8785encode (&wire_enc); 103 if (NULL == salt) 104 { 105 GNUNET_CRYPTO_hash (wire_enc, 106 len, 107 hc); 108 } 109 else 110 { 111 if (GNUNET_YES != 112 GNUNET_CRYPTO_kdf (hc, 113 sizeof (*hc), 114 salt, 115 strlen (salt) + 1, 116 wire_enc, 117 len, 118 NULL, 119 0)) 120 { 121 GNUNET_break (0); 122 free (wire_enc); 123 return GNUNET_SYSERR; 124 } 125 } 126 free (wire_enc); 127 return GNUNET_OK; 128 } 129 130 131 /** 132 * Replace "forgettable" parts of a JSON object with their salted hash. 133 * 134 * @param[in] in some JSON value 135 * @param[out] out resulting JSON value 136 * @return #GNUNET_OK on success, 137 * #GNUNET_NO if @a json was not hash-able 138 * #GNUNET_SYSERR on failure 139 */ 140 static enum GNUNET_GenericReturnValue 141 forget (const json_t *in, 142 json_t **out) 143 { 144 if (json_is_real (in)) 145 { 146 /* floating point is not allowed! */ 147 GNUNET_break_op (0); 148 return GNUNET_NO; 149 } 150 if (json_is_array (in)) 151 { 152 /* array is a JSON array */ 153 size_t index; 154 json_t *value; 155 json_t *ret; 156 157 ret = json_array (); 158 if (NULL == ret) 159 { 160 GNUNET_break (0); 161 return GNUNET_SYSERR; 162 } 163 json_array_foreach (in, index, value) { 164 enum GNUNET_GenericReturnValue iret; 165 json_t *t; 166 167 iret = forget (value, 168 &t); 169 if (GNUNET_OK != iret) 170 { 171 json_decref (ret); 172 return iret; 173 } 174 if (0 != json_array_append_new (ret, 175 t)) 176 { 177 GNUNET_break (0); 178 json_decref (ret); 179 return GNUNET_SYSERR; 180 } 181 } 182 *out = ret; 183 return GNUNET_OK; 184 } 185 if (json_is_object (in)) 186 { 187 json_t *ret; 188 const char *key; 189 json_t *value; 190 json_t *fg; 191 json_t *rx; 192 193 fg = json_object_get (in, 194 "$forgettable"); 195 rx = json_object_get (in, 196 "$forgotten"); 197 if (NULL != rx) 198 { 199 rx = json_deep_copy (rx); /* should be shallow 200 by structure, but 201 deep copy is safer */ 202 if (NULL == rx) 203 { 204 GNUNET_break (0); 205 return GNUNET_SYSERR; 206 } 207 } 208 ret = json_object (); 209 if (NULL == ret) 210 { 211 GNUNET_break (0); 212 return GNUNET_SYSERR; 213 } 214 json_object_foreach ((json_t*) in, key, value) { 215 json_t *t; 216 json_t *salt; 217 enum GNUNET_GenericReturnValue iret; 218 219 if (fg == value) 220 continue; /* skip! */ 221 if (rx == value) 222 continue; /* skip! */ 223 if ( (NULL != rx) && 224 (NULL != 225 json_object_get (rx, 226 key)) ) 227 { 228 (void) json_object_del (ret, 229 key); 230 continue; /* already forgotten earlier */ 231 } 232 iret = forget (value, 233 &t); 234 if (GNUNET_OK != iret) 235 { 236 json_decref (ret); 237 json_decref (rx); 238 return iret; 239 } 240 if ( (NULL != fg) && 241 (NULL != (salt = json_object_get (fg, 242 key))) ) 243 { 244 /* 't' is to be forgotten! */ 245 struct GNUNET_HashCode hc; 246 247 if (! json_is_string (salt)) 248 { 249 GNUNET_break_op (0); 250 json_decref (ret); 251 json_decref (rx); 252 json_decref (t); 253 return GNUNET_NO; 254 } 255 iret = dump_and_hash (t, 256 json_string_value (salt), 257 &hc); 258 if (GNUNET_OK != iret) 259 { 260 json_decref (ret); 261 json_decref (rx); 262 json_decref (t); 263 return iret; 264 } 265 json_decref (t); 266 /* scrub salt */ 267 if (0 != 268 json_object_del (fg, 269 key)) 270 { 271 GNUNET_break_op (0); 272 json_decref (ret); 273 json_decref (rx); 274 return GNUNET_NO; 275 } 276 if (NULL == rx) 277 rx = json_object (); 278 if (NULL == rx) 279 { 280 GNUNET_break (0); 281 json_decref (ret); 282 return GNUNET_SYSERR; 283 } 284 if (0 != 285 json_object_set_new (rx, 286 key, 287 GNUNET_JSON_from_data_auto (&hc))) 288 { 289 GNUNET_break (0); 290 json_decref (ret); 291 json_decref (rx); 292 return GNUNET_SYSERR; 293 } 294 } 295 else 296 { 297 /* 't' to be used without 'forgetting' */ 298 if (0 != 299 json_object_set_new (ret, 300 key, 301 t)) 302 { 303 GNUNET_break (0); 304 json_decref (ret); 305 json_decref (rx); 306 return GNUNET_SYSERR; 307 } 308 } 309 } /* json_object_foreach */ 310 if ( (NULL != rx) && 311 (0 != 312 json_object_set_new (ret, 313 "$forgotten", 314 rx)) ) 315 { 316 GNUNET_break (0); 317 json_decref (ret); 318 return GNUNET_SYSERR; 319 } 320 *out = ret; 321 return GNUNET_OK; 322 } 323 *out = json_incref ((json_t *) in); 324 return GNUNET_OK; 325 } 326 327 328 enum GNUNET_GenericReturnValue 329 TALER_JSON_contract_hash (const json_t *json, 330 struct TALER_PrivateContractHashP *hc) 331 { 332 enum GNUNET_GenericReturnValue ret; 333 json_t *cjson; 334 json_t *dc; 335 336 dc = json_deep_copy (json); 337 ret = forget (dc, 338 &cjson); 339 json_decref (dc); 340 if (GNUNET_OK != ret) 341 return ret; 342 ret = dump_and_hash (cjson, 343 NULL, 344 &hc->hash); 345 json_decref (cjson); 346 return ret; 347 } 348 349 350 enum GNUNET_GenericReturnValue 351 TALER_JSON_contract_mark_forgettable (json_t *json, 352 const char *field) 353 { 354 json_t *fg; 355 struct GNUNET_ShortHashCode salt; 356 357 if (! json_is_object (json)) 358 { 359 GNUNET_break (0); 360 return GNUNET_SYSERR; 361 } 362 /* check field name is legal for forgettable field */ 363 for (const char *f = field; '\0' != *f; f++) 364 { 365 char c = *f; 366 367 if ( (c >= 'a') && (c <= 'z') ) 368 continue; 369 if ( (c >= 'A') && (c <= 'Z') ) 370 continue; 371 if ( (c >= '0') && (c <= '9') ) 372 continue; 373 if ('_' == c) 374 continue; 375 GNUNET_break (0); 376 return GNUNET_SYSERR; 377 } 378 if (NULL == json_object_get (json, 379 field)) 380 { 381 /* field must exist */ 382 GNUNET_break (0); 383 return GNUNET_SYSERR; 384 } 385 fg = json_object_get (json, 386 "$forgettable"); 387 if (NULL == fg) 388 { 389 fg = json_object (); 390 if (0 != 391 json_object_set_new (json, 392 "$forgettable", 393 fg)) 394 { 395 GNUNET_break (0); 396 return GNUNET_SYSERR; 397 } 398 } 399 400 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 401 &salt, 402 sizeof (salt)); 403 if (0 != 404 json_object_set_new (fg, 405 field, 406 GNUNET_JSON_from_data_auto (&salt))) 407 { 408 GNUNET_break (0); 409 return GNUNET_SYSERR; 410 } 411 return GNUNET_OK; 412 } 413 414 415 enum GNUNET_GenericReturnValue 416 TALER_JSON_contract_part_forget (json_t *json, 417 const char *field) 418 { 419 json_t *fg; 420 const json_t *part; 421 json_t *fp; 422 json_t *rx; 423 struct GNUNET_HashCode hc; 424 const char *salt; 425 enum GNUNET_GenericReturnValue ret; 426 427 if (! json_is_object (json)) 428 { 429 GNUNET_break (0); 430 return GNUNET_SYSERR; 431 } 432 if (NULL == (part = json_object_get (json, 433 field))) 434 { 435 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 436 "Did not find field `%s' we were asked to forget\n", 437 field); 438 return GNUNET_SYSERR; 439 } 440 fg = json_object_get (json, 441 "$forgettable"); 442 if (NULL == fg) 443 { 444 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 445 "Did not find '$forgettable' attribute trying to forget field `%s'\n", 446 field); 447 return GNUNET_SYSERR; 448 } 449 rx = json_object_get (json, 450 "$forgotten"); 451 if (NULL == rx) 452 { 453 rx = json_object (); 454 if (0 != 455 json_object_set_new (json, 456 "$forgotten", 457 rx)) 458 { 459 GNUNET_break (0); 460 return GNUNET_SYSERR; 461 } 462 } 463 if (NULL != 464 json_object_get (rx, 465 field)) 466 { 467 if (! json_is_null (json_object_get (json, 468 field))) 469 { 470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 471 "Field `%s' market as forgotten, but still exists!\n", 472 field); 473 return GNUNET_SYSERR; 474 } 475 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 476 "Already forgot field `%s'\n", 477 field); 478 return GNUNET_NO; 479 } 480 salt = json_string_value (json_object_get (fg, 481 field)); 482 if (NULL == salt) 483 { 484 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 485 "Did not find required salt to forget field `%s'\n", 486 field); 487 return GNUNET_SYSERR; 488 } 489 490 /* need to recursively forget to compute 'hc' */ 491 ret = forget (part, 492 &fp); 493 if (GNUNET_OK != ret) 494 return ret; 495 if (GNUNET_OK != 496 dump_and_hash (fp, 497 salt, 498 &hc)) 499 { 500 json_decref (fp); 501 GNUNET_break (0); 502 return GNUNET_SYSERR; 503 } 504 json_decref (fp); 505 /* drop salt */ 506 if (0 != 507 json_object_del (fg, 508 field)) 509 { 510 json_decref (fp); 511 GNUNET_break (0); 512 return GNUNET_SYSERR; 513 } 514 515 /* remember field as 'forgotten' */ 516 if (0 != 517 json_object_set_new (rx, 518 field, 519 GNUNET_JSON_from_data_auto (&hc))) 520 { 521 GNUNET_break (0); 522 return GNUNET_SYSERR; 523 } 524 /* finally, set 'forgotten' field to null */ 525 if (0 != 526 json_object_del (json, 527 field)) 528 { 529 GNUNET_break (0); 530 return GNUNET_SYSERR; 531 } 532 return GNUNET_OK; 533 } 534 535 536 /** 537 * Parse a json path. 538 * 539 * @param obj the object that the path is relative to. 540 * @param prev the parent of @e obj. 541 * @param path the path to parse. 542 * @param cb the callback to call, if we get to the end of @e path. 543 * @param cb_cls the closure for the callback. 544 * @return #GNUNET_OK on success, #GNUNET_SYSERR if @e path is malformed. 545 */ 546 static enum GNUNET_GenericReturnValue 547 parse_path (json_t *obj, 548 json_t *prev, 549 const char *path, 550 TALER_JSON_ExpandPathCallback cb, 551 void *cb_cls) 552 { 553 char *id = GNUNET_strdup (path); 554 char *next_id = strchr (id, 555 '.'); 556 char *next_path; 557 char *bracket; 558 json_t *next_obj = NULL; 559 char *next_dot; 560 561 GNUNET_assert (NULL != id); /* make stupid compiler happy */ 562 if (NULL == next_id) 563 { 564 cb (cb_cls, 565 id, 566 prev); 567 GNUNET_free (id); 568 return GNUNET_OK; 569 } 570 bracket = strchr (next_id, 571 '['); 572 *next_id = '\0'; 573 next_id++; 574 next_path = GNUNET_strdup (next_id); 575 next_dot = strchr (next_id, 576 '.'); 577 if (NULL != next_dot) 578 *next_dot = '\0'; 579 /* If this is the first time this is called, make sure id is "$" */ 580 if ( (NULL == prev) && 581 (0 != strcmp (id, 582 "$"))) 583 { 584 GNUNET_free (id); 585 GNUNET_free (next_path); 586 return GNUNET_SYSERR; 587 } 588 589 /* Check for bracketed indices */ 590 if (NULL != bracket) 591 { 592 json_t *array; 593 char *end_bracket = strchr (bracket, 594 ']'); 595 if (NULL == end_bracket) 596 { 597 GNUNET_free (id); 598 GNUNET_free (next_path); 599 return GNUNET_SYSERR; 600 } 601 *end_bracket = '\0'; 602 603 *bracket = '\0'; 604 bracket++; 605 606 array = json_object_get (obj, 607 next_id); 608 if (0 == strcmp (bracket, 609 "*")) 610 { 611 size_t index; 612 json_t *value; 613 int ret = GNUNET_OK; 614 615 json_array_foreach (array, index, value) { 616 ret = parse_path (value, 617 obj, 618 next_path, 619 cb, 620 cb_cls); 621 if (GNUNET_OK != ret) 622 { 623 GNUNET_free (id); 624 GNUNET_free (next_path); 625 return ret; 626 } 627 } 628 } 629 else 630 { 631 unsigned int index; 632 char dummy; 633 634 if (1 != sscanf (bracket, 635 "%u%c", 636 &index, 637 &dummy)) 638 { 639 GNUNET_free (id); 640 GNUNET_free (next_path); 641 return GNUNET_SYSERR; 642 } 643 next_obj = json_array_get (array, 644 index); 645 } 646 } 647 else 648 { 649 /* No brackets, so just fetch the object by name */ 650 next_obj = json_object_get (obj, 651 next_id); 652 } 653 654 if (NULL != next_obj) 655 { 656 int ret = parse_path (next_obj, 657 obj, 658 next_path, 659 cb, 660 cb_cls); 661 GNUNET_free (id); 662 GNUNET_free (next_path); 663 return ret; 664 } 665 GNUNET_free (id); 666 GNUNET_free (next_path); 667 return GNUNET_OK; 668 } 669 670 671 enum GNUNET_GenericReturnValue 672 TALER_JSON_expand_path (json_t *json, 673 const char *path, 674 TALER_JSON_ExpandPathCallback cb, 675 void *cb_cls) 676 { 677 return parse_path (json, 678 NULL, 679 path, 680 cb, 681 cb_cls); 682 } 683 684 685 enum TALER_ErrorCode 686 TALER_JSON_get_error_code (const json_t *json) 687 { 688 const json_t *jc; 689 690 if (NULL == json) 691 return TALER_EC_GENERIC_INVALID_RESPONSE; 692 jc = json_object_get (json, "code"); 693 /* The caller already knows that the JSON represents an error, 694 so we are dealing with a missing error code here. */ 695 if (NULL == jc) 696 { 697 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 698 "Expected Taler error code `code' in JSON, but field does not exist!\n"); 699 return TALER_EC_INVALID; 700 } 701 if (json_is_integer (jc)) 702 return (enum TALER_ErrorCode) (int) json_integer_value (jc); 703 GNUNET_break_op (0); 704 return TALER_EC_INVALID; 705 } 706 707 708 const char * 709 TALER_JSON_get_error_hint (const json_t *json) 710 { 711 const json_t *jc; 712 713 if (NULL == json) 714 return NULL; 715 jc = json_object_get (json, 716 "hint"); 717 if (NULL == jc) 718 return NULL; /* no hint, is allowed */ 719 if (! json_is_string (jc)) 720 { 721 /* Hints must be strings */ 722 GNUNET_break_op (0); 723 return NULL; 724 } 725 return json_string_value (jc); 726 } 727 728 729 enum TALER_ErrorCode 730 TALER_JSON_get_error_code2 (const void *data, 731 size_t data_size) 732 { 733 json_t *json; 734 enum TALER_ErrorCode ec; 735 json_error_t err; 736 737 json = json_loadb (data, 738 data_size, 739 JSON_REJECT_DUPLICATES, 740 &err); 741 if (NULL == json) 742 return TALER_EC_INVALID; 743 ec = TALER_JSON_get_error_code (json); 744 json_decref (json); 745 if (ec == TALER_EC_NONE) 746 return TALER_EC_INVALID; 747 return ec; 748 } 749 750 751 void 752 TALER_deposit_policy_hash (const json_t *policy, 753 struct TALER_ExtensionPolicyHashP *ech) 754 { 755 GNUNET_assert (GNUNET_OK == 756 dump_and_hash (policy, 757 "taler-extensions-policy", 758 &ech->hash)); 759 } 760 761 762 char * 763 TALER_JSON_canonicalize (const json_t *input) 764 { 765 char *wire_enc; 766 767 if (NULL == (wire_enc = json_dumps (input, 768 JSON_ENCODE_ANY 769 | JSON_COMPACT 770 | JSON_SORT_KEYS))) 771 { 772 GNUNET_break (0); 773 return NULL; 774 } 775 TALER_rfc8785encode (&wire_enc); 776 return wire_enc; 777 } 778 779 780 enum GNUNET_GenericReturnValue 781 TALER_JSON_extensions_manifests_hash (const json_t *manifests, 782 struct TALER_ExtensionManifestsHashP *ech) 783 { 784 return dump_and_hash (manifests, 785 "taler-extensions-manifests", 786 &ech->hash); 787 } 788 789 790 /* End of json/json.c */