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