donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

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 */