donau

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

commit afc7557882e41c13ae1c59f29b2292eb696a87dc
parent fb51789539086221404b84628b6ab031e9398e78
Author: Pius Loosli <loosp2@bfh.ch>
Date:   Sat,  6 Jan 2024 21:36:14 +0100

[deps] add json from exchange (can be removed again if json is linked against json from exchange)

Diffstat:
Msrc/json/Makefile.am | 5++++-
Msrc/json/json.c | 763++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Asrc/json/json_helper.c | 1621+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/json/json_pack.c | 324+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 2710 insertions(+), 3 deletions(-)

diff --git a/src/json/Makefile.am b/src/json/Makefile.am @@ -10,7 +10,10 @@ lib_LTLIBRARIES = \ libtalerjson.la libtalerjson_la_SOURCES = \ - json.c + json.c \ + json_helper.c \ + json_pack.c + libtalerjson_la_LDFLAGS = \ -version-info 1:0:1 \ -no-undefined diff --git a/src/json/json.c b/src/json/json.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2023 Taler Systems SA + Copyright (C) 2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -21,9 +21,768 @@ */ #include "taler/platform.h" #include <gnunet/gnunet_util_lib.h> -#include <taler/taler_util.h> +#include "taler/taler_util.h" #include "taler/taler_json_lib.h" #include <unistr.h> +/** + * Check if @a json contains a 'real' value anywhere. + * + * @param json json to check + * @return true if a real is in it somewhere + */ +static bool +contains_real (const json_t *json) +{ + if (json_is_real (json)) + return true; + if (json_is_object (json)) + { + json_t *member; + const char *name; + + json_object_foreach ((json_t *) json, name, member) + if (contains_real (member)) + return true; + return false; + } + if (json_is_array (json)) + { + json_t *member; + size_t index; + + json_array_foreach ((json_t *) json, index, member) + if (contains_real (member)) + return true; + return false; + } + return false; +} + + +/** + * Dump the @a json to a string and hash it. + * + * @param json value to hash + * @param salt salt value to include when using HKDF, + * NULL to not use any salt and to use SHA512 + * @param[out] hc where to store the hash + * @return #GNUNET_OK on success, + * #GNUNET_NO if @a json was not hash-able + * #GNUNET_SYSERR on failure + */ +static enum GNUNET_GenericReturnValue +dump_and_hash (const json_t *json, + const char *salt, + struct GNUNET_HashCode *hc) +{ + char *wire_enc; + size_t len; + + if (NULL == json) + { + GNUNET_break_op (0); + return GNUNET_NO; + } + if (contains_real (json)) + { + GNUNET_break_op (0); + return GNUNET_NO; + } + if (NULL == (wire_enc = json_dumps (json, + JSON_ENCODE_ANY + | JSON_COMPACT + | JSON_SORT_KEYS))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + len = TALER_rfc8785encode (&wire_enc); + if (NULL == salt) + { + GNUNET_CRYPTO_hash (wire_enc, + len, + hc); + } + else + { + if (GNUNET_YES != + GNUNET_CRYPTO_kdf (hc, + sizeof (*hc), + salt, + strlen (salt) + 1, + wire_enc, + len, + NULL, + 0)) + { + GNUNET_break (0); + free (wire_enc); + return GNUNET_SYSERR; + } + } + free (wire_enc); + return GNUNET_OK; +} + + +/** + * Replace "forgettable" parts of a JSON object with their salted hash. + * + * @param[in] in some JSON value + * @param[out] out resulting JSON value + * @return #GNUNET_OK on success, + * #GNUNET_NO if @a json was not hash-able + * #GNUNET_SYSERR on failure + */ +static enum GNUNET_GenericReturnValue +forget (const json_t *in, + json_t **out) +{ + if (json_is_real (in)) + { + /* floating point is not allowed! */ + GNUNET_break_op (0); + return GNUNET_NO; + } + if (json_is_array (in)) + { + /* array is a JSON array */ + size_t index; + json_t *value; + json_t *ret; + + ret = json_array (); + if (NULL == ret) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + json_array_foreach (in, index, value) { + enum GNUNET_GenericReturnValue iret; + json_t *t; + + iret = forget (value, + &t); + if (GNUNET_OK != iret) + { + json_decref (ret); + return iret; + } + if (0 != json_array_append_new (ret, + t)) + { + GNUNET_break (0); + json_decref (ret); + return GNUNET_SYSERR; + } + } + *out = ret; + return GNUNET_OK; + } + if (json_is_object (in)) + { + json_t *ret; + const char *key; + json_t *value; + json_t *fg; + json_t *rx; + + fg = json_object_get (in, + "$forgettable"); + rx = json_object_get (in, + "$forgotten"); + if (NULL != rx) + { + rx = json_deep_copy (rx); /* should be shallow + by structure, but + deep copy is safer */ + if (NULL == rx) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + ret = json_object (); + if (NULL == ret) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + json_object_foreach ((json_t*) in, key, value) { + json_t *t; + json_t *salt; + enum GNUNET_GenericReturnValue iret; + + if (fg == value) + continue; /* skip! */ + if (rx == value) + continue; /* skip! */ + if ( (NULL != rx) && + (NULL != + json_object_get (rx, + key)) ) + { + (void) json_object_del (ret, + key); + continue; /* already forgotten earlier */ + } + iret = forget (value, + &t); + if (GNUNET_OK != iret) + { + json_decref (ret); + json_decref (rx); + return iret; + } + if ( (NULL != fg) && + (NULL != (salt = json_object_get (fg, + key))) ) + { + /* 't' is to be forgotten! */ + struct GNUNET_HashCode hc; + + if (! json_is_string (salt)) + { + GNUNET_break_op (0); + json_decref (ret); + json_decref (rx); + json_decref (t); + return GNUNET_NO; + } + iret = dump_and_hash (t, + json_string_value (salt), + &hc); + if (GNUNET_OK != iret) + { + json_decref (ret); + json_decref (rx); + json_decref (t); + return iret; + } + json_decref (t); + /* scrub salt */ + if (0 != + json_object_del (fg, + key)) + { + GNUNET_break_op (0); + json_decref (ret); + json_decref (rx); + return GNUNET_NO; + } + if (NULL == rx) + rx = json_object (); + if (NULL == rx) + { + GNUNET_break (0); + json_decref (ret); + return GNUNET_SYSERR; + } + if (0 != + json_object_set_new (rx, + key, + GNUNET_JSON_from_data_auto (&hc))) + { + GNUNET_break (0); + json_decref (ret); + json_decref (rx); + return GNUNET_SYSERR; + } + } + else + { + /* 't' to be used without 'forgetting' */ + if (0 != + json_object_set_new (ret, + key, + t)) + { + GNUNET_break (0); + json_decref (ret); + json_decref (rx); + return GNUNET_SYSERR; + } + } + } /* json_object_foreach */ + if ( (NULL != rx) && + (0 != + json_object_set_new (ret, + "$forgotten", + rx)) ) + { + GNUNET_break (0); + json_decref (ret); + return GNUNET_SYSERR; + } + *out = ret; + return GNUNET_OK; + } + *out = json_incref ((json_t *) in); + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_JSON_contract_hash (const json_t *json, + struct TALER_PrivateContractHashP *hc) +{ + enum GNUNET_GenericReturnValue ret; + json_t *cjson; + json_t *dc; + + dc = json_deep_copy (json); + ret = forget (dc, + &cjson); + json_decref (dc); + if (GNUNET_OK != ret) + return ret; + ret = dump_and_hash (cjson, + NULL, + &hc->hash); + json_decref (cjson); + return ret; +} + + +enum GNUNET_GenericReturnValue +TALER_JSON_contract_mark_forgettable (json_t *json, + const char *field) +{ + json_t *fg; + struct GNUNET_ShortHashCode salt; + + if (! json_is_object (json)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* check field name is legal for forgettable field */ + for (const char *f = field; '\0' != *f; f++) + { + char c = *f; + + if ( (c >= 'a') && (c <= 'z') ) + continue; + if ( (c >= 'A') && (c <= 'Z') ) + continue; + if ( (c >= '0') && (c <= '9') ) + continue; + if ('_' == c) + continue; + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (NULL == json_object_get (json, + field)) + { + /* field must exist */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + fg = json_object_get (json, + "$forgettable"); + if (NULL == fg) + { + fg = json_object (); + if (0 != + json_object_set_new (json, + "$forgettable", + fg)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &salt, + sizeof (salt)); + if (0 != + json_object_set_new (fg, + field, + GNUNET_JSON_from_data_auto (&salt))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_JSON_contract_part_forget (json_t *json, + const char *field) +{ + json_t *fg; + const json_t *part; + json_t *fp; + json_t *rx; + struct GNUNET_HashCode hc; + const char *salt; + enum GNUNET_GenericReturnValue ret; + + if (! json_is_object (json)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (NULL == (part = json_object_get (json, + field))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Did not find field `%s' we were asked to forget\n", + field); + return GNUNET_SYSERR; + } + fg = json_object_get (json, + "$forgettable"); + if (NULL == fg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Did not find '$forgettable' attribute trying to forget field `%s'\n", + field); + return GNUNET_SYSERR; + } + rx = json_object_get (json, + "$forgotten"); + if (NULL == rx) + { + rx = json_object (); + if (0 != + json_object_set_new (json, + "$forgotten", + rx)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + if (NULL != + json_object_get (rx, + field)) + { + if (! json_is_null (json_object_get (json, + field))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Field `%s' market as forgotten, but still exists!\n", + field); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Already forgot field `%s'\n", + field); + return GNUNET_NO; + } + salt = json_string_value (json_object_get (fg, + field)); + if (NULL == salt) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Did not find required salt to forget field `%s'\n", + field); + return GNUNET_SYSERR; + } + + /* need to recursively forget to compute 'hc' */ + ret = forget (part, + &fp); + if (GNUNET_OK != ret) + return ret; + if (GNUNET_OK != + dump_and_hash (fp, + salt, + &hc)) + { + json_decref (fp); + GNUNET_break (0); + return GNUNET_SYSERR; + } + json_decref (fp); + /* drop salt */ + if (0 != + json_object_del (fg, + field)) + { + json_decref (fp); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + /* remember field as 'forgotten' */ + if (0 != + json_object_set_new (rx, + field, + GNUNET_JSON_from_data_auto (&hc))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* finally, set 'forgotten' field to null */ + if (0 != + json_object_del (json, + field)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Parse a json path. + * + * @param obj the object that the path is relative to. + * @param prev the parent of @e obj. + * @param path the path to parse. + * @param cb the callback to call, if we get to the end of @e path. + * @param cb_cls the closure for the callback. + * @return #GNUNET_OK on success, #GNUNET_SYSERR if @e path is malformed. + */ +static enum GNUNET_GenericReturnValue +parse_path (json_t *obj, + json_t *prev, + const char *path, + TALER_JSON_ExpandPathCallback cb, + void *cb_cls) +{ + char *id = GNUNET_strdup (path); + char *next_id = strchr (id, + '.'); + char *next_path; + char *bracket; + json_t *next_obj = NULL; + char *next_dot; + + GNUNET_assert (NULL != id); /* make stupid compiler happy */ + if (NULL == next_id) + { + cb (cb_cls, + id, + prev); + GNUNET_free (id); + return GNUNET_OK; + } + bracket = strchr (next_id, + '['); + *next_id = '\0'; + next_id++; + next_path = GNUNET_strdup (next_id); + next_dot = strchr (next_id, + '.'); + if (NULL != next_dot) + *next_dot = '\0'; + /* If this is the first time this is called, make sure id is "$" */ + if ( (NULL == prev) && + (0 != strcmp (id, + "$"))) + { + GNUNET_free (id); + GNUNET_free (next_path); + return GNUNET_SYSERR; + } + + /* Check for bracketed indices */ + if (NULL != bracket) + { + char *end_bracket = strchr (bracket, + ']'); + if (NULL == end_bracket) + { + GNUNET_free (id); + GNUNET_free (next_path); + return GNUNET_SYSERR; + } + *end_bracket = '\0'; + + *bracket = '\0'; + bracket++; + + json_t *array = json_object_get (obj, + next_id); + if (0 == strcmp (bracket, + "*")) + { + size_t index; + json_t *value; + int ret = GNUNET_OK; + + json_array_foreach (array, index, value) { + ret = parse_path (value, + obj, + next_path, + cb, + cb_cls); + if (GNUNET_OK != ret) + { + GNUNET_free (id); + GNUNET_free (next_path); + return ret; + } + } + } + else + { + unsigned int index; + char dummy; + + if (1 != sscanf (bracket, + "%u%c", + &index, + &dummy)) + { + GNUNET_free (id); + GNUNET_free (next_path); + return GNUNET_SYSERR; + } + next_obj = json_array_get (array, + index); + } + } + else + { + /* No brackets, so just fetch the object by name */ + next_obj = json_object_get (obj, + next_id); + } + + if (NULL != next_obj) + { + int ret = parse_path (next_obj, + obj, + next_path, + cb, + cb_cls); + GNUNET_free (id); + GNUNET_free (next_path); + return ret; + } + GNUNET_free (id); + GNUNET_free (next_path); + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_JSON_expand_path (json_t *json, + const char *path, + TALER_JSON_ExpandPathCallback cb, + void *cb_cls) +{ + return parse_path (json, + NULL, + path, + cb, + cb_cls); +} + + +enum TALER_ErrorCode +TALER_JSON_get_error_code (const json_t *json) +{ + const json_t *jc; + + if (NULL == json) + return TALER_EC_GENERIC_INVALID_RESPONSE; + jc = json_object_get (json, "code"); + /* The caller already knows that the JSON represents an error, + so we are dealing with a missing error code here. */ + if (NULL == jc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Expected Taler error code `code' in JSON, but field does not exist!\n"); + return TALER_EC_INVALID; + } + if (json_is_integer (jc)) + return (enum TALER_ErrorCode) json_integer_value (jc); + GNUNET_break_op (0); + return TALER_EC_INVALID; +} + + +const char * +TALER_JSON_get_error_hint (const json_t *json) +{ + const json_t *jc; + + if (NULL == json) + return NULL; + jc = json_object_get (json, + "hint"); + if (NULL == jc) + return NULL; /* no hint, is allowed */ + if (! json_is_string (jc)) + { + /* Hints must be strings */ + GNUNET_break_op (0); + return NULL; + } + return json_string_value (jc); +} + + +enum TALER_ErrorCode +TALER_JSON_get_error_code2 (const void *data, + size_t data_size) +{ + json_t *json; + enum TALER_ErrorCode ec; + json_error_t err; + + json = json_loadb (data, + data_size, + JSON_REJECT_DUPLICATES, + &err); + if (NULL == json) + return TALER_EC_INVALID; + ec = TALER_JSON_get_error_code (json); + json_decref (json); + if (ec == TALER_EC_NONE) + return TALER_EC_INVALID; + return ec; +} + + +void +TALER_deposit_policy_hash (const json_t *policy, + struct TALER_ExtensionPolicyHashP *ech) +{ + GNUNET_assert (GNUNET_OK == + dump_and_hash (policy, + "taler-extensions-policy", + &ech->hash)); +} + + +char * +TALER_JSON_canonicalize (const json_t *input) +{ + char *wire_enc; + + if (NULL == (wire_enc = json_dumps (input, + JSON_ENCODE_ANY + | JSON_COMPACT + | JSON_SORT_KEYS))) + { + GNUNET_break (0); + return NULL; + } + TALER_rfc8785encode (&wire_enc); + return wire_enc; +} + + +enum GNUNET_GenericReturnValue +TALER_JSON_extensions_manifests_hash (const json_t *manifests, + struct TALER_ExtensionManifestsHashP *ech) +{ + return dump_and_hash (manifests, + "taler-extensions-manifests", + &ech->hash); +} + + /* End of json/json.c */ diff --git a/src/json/json_helper.c b/src/json/json_helper.c @@ -0,0 +1,1621 @@ +/* + This file is part of TALER + Copyright (C) 2014-2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file json/json_helper.c + * @brief helper functions to generate specifications to parse + * Taler-specific JSON objects with libgnunetjson + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_util.h" +#include "taler/taler_json_lib.h" + + +/** + * Convert string value to numeric cipher value. + * + * @param cipher_s input string + * @return numeric cipher value + */ +static enum GNUNET_CRYPTO_BlindSignatureAlgorithm +string_to_cipher (const char *cipher_s) +{ + if ((0 == strcasecmp (cipher_s, + "RSA")) || + (0 == strcasecmp (cipher_s, + "RSA+age_restricted"))) + return GNUNET_CRYPTO_BSA_RSA; + if ((0 == strcasecmp (cipher_s, + "CS")) || + (0 == strcasecmp (cipher_s, + "CS+age_restricted"))) + return GNUNET_CRYPTO_BSA_CS; + return GNUNET_CRYPTO_BSA_INVALID; +} + + +json_t * +TALER_JSON_from_amount (const struct TALER_Amount *amount) +{ + char *amount_str = TALER_amount_to_string (amount); + + GNUNET_assert (NULL != amount_str); + { + json_t *j = json_string (amount_str); + + GNUNET_free (amount_str); + return j; + } +} + + +/** + * Parse given JSON object to Amount + * + * @param cls closure, expected currency, or NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_amount (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *currency = cls; + struct TALER_Amount *r_amount = spec->ptr; + + (void) cls; + if (! json_is_string (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_string_to_amount (json_string_value (root), + r_amount)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (NULL != currency) && + (0 != + strcasecmp (currency, + r_amount->currency)) ) + { + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Expected currency `%s', but amount used currency `%s' in field `%s'\n", + currency, + r_amount->currency, + spec->field); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_amount (const char *name, + const char *currency, + struct TALER_Amount *r_amount) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_amount, + .cleaner = NULL, + .cls = (void *) currency, + .field = name, + .ptr = r_amount, + .ptr_size = 0, + .size_ptr = NULL + }; + + GNUNET_assert (NULL != currency); + return ret; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_amount_any (const char *name, + struct TALER_Amount *r_amount) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_amount, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = r_amount, + .ptr_size = 0, + .size_ptr = NULL + }; + + return ret; +} + + +/** + * Parse given JSON object to currency spec. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_cspec (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_CurrencySpecification *r_cspec = spec->ptr; + const char *currency = spec->cls; + const char *name; + uint32_t fid; + uint32_t fnd; + uint32_t ftzd; + const json_t *map; + struct GNUNET_JSON_Specification gspec[] = { + GNUNET_JSON_spec_string ("name", + &name), + GNUNET_JSON_spec_uint32 ("num_fractional_input_digits", + &fid), + GNUNET_JSON_spec_uint32 ("num_fractional_normal_digits", + &fnd), + GNUNET_JSON_spec_uint32 ("num_fractional_trailing_zero_digits", + &ftzd), + GNUNET_JSON_spec_object_const ("alt_unit_names", + &map), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + memset (r_cspec->currency, + 0, + sizeof (r_cspec->currency)); + if (GNUNET_OK != + GNUNET_JSON_parse (root, + gspec, + &emsg, + &eline)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse %s at %u: %s\n", + spec[eline].field, + eline, + emsg); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (strlen (currency) >= TALER_CURRENCY_LEN) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (fid > TALER_AMOUNT_FRAC_LEN) || + (fnd > TALER_AMOUNT_FRAC_LEN) || + (ftzd > TALER_AMOUNT_FRAC_LEN) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_check_currency (currency)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + strcpy (r_cspec->currency, + currency); + if (GNUNET_OK != + TALER_check_currency_scale_map (map)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + r_cspec->name = GNUNET_strdup (name); + r_cspec->map_alt_unit_names = json_incref ((json_t *) map); + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing encrypted contract. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_cspec (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_CurrencySpecification *cspec = spec->ptr; + + (void) cls; + GNUNET_free (cspec->name); + json_decref (cspec->map_alt_unit_names); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_currency_specification ( + const char *name, + const char *currency, + struct TALER_CurrencySpecification *r_cspec) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_cspec, + .cleaner = &clean_cspec, + .cls = (void *) currency, + .field = name, + .ptr = r_cspec, + .ptr_size = sizeof (*r_cspec), + .size_ptr = NULL + }; + + memset (r_cspec, + 0, + sizeof (*r_cspec)); + return ret; +} + + +static enum GNUNET_GenericReturnValue +parse_denomination_group (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationGroup *group = spec->ptr; + const char *cipher; + const char *currency = cls; + bool age_mask_missing = false; + bool has_age_restricted_suffix = false; + struct GNUNET_JSON_Specification gspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + TALER_JSON_spec_amount ("value", + currency, + &group->value), + TALER_JSON_SPEC_DENOM_FEES ("fee", + currency, + &group->fees), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("age_mask", + &group->age_mask.bits), + &age_mask_missing), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + gspec, + &emsg, + &eline)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse %s at %u: %s\n", + spec[eline].field, + eline, + emsg); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + group->cipher = string_to_cipher (cipher); + if (GNUNET_CRYPTO_BSA_INVALID == group->cipher) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* age_mask and suffix must be consistent */ + has_age_restricted_suffix = + (NULL != strstr (cipher, "+age_restricted")); + if (has_age_restricted_suffix && age_mask_missing) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (age_mask_missing) + group->age_mask.bits = 0; + + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_group (const char *name, + const char *currency, + struct TALER_DenominationGroup *group) +{ + struct GNUNET_JSON_Specification ret = { + .cls = (void *) currency, + .parser = &parse_denomination_group, + .field = name, + .ptr = group, + .ptr_size = sizeof(*group) + }; + + return ret; +} + + +/** + * Parse given JSON object to an encrypted contract. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_econtract (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_EncryptedContract *econtract = spec->ptr; + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_varsize ("econtract", + &econtract->econtract, + &econtract->econtract_size), + GNUNET_JSON_spec_fixed_auto ("econtract_sig", + &econtract->econtract_sig), + GNUNET_JSON_spec_fixed_auto ("contract_pub", + &econtract->contract_pub), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + (void) cls; + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing encrypted contract. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_econtract (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_EncryptedContract *econtract = spec->ptr; + + (void) cls; + GNUNET_free (econtract->econtract); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_econtract (const char *name, + struct TALER_EncryptedContract *econtract) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_econtract, + .cleaner = &clean_econtract, + .field = name, + .ptr = econtract + }; + + return ret; +} + + +/** + * Parse given JSON object to an age commitmnet + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_age_commitment (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_AgeCommitment *age_commitment = spec->ptr; + json_t *pk; + unsigned int idx; + size_t num; + + (void) cls; + if ( (NULL == root) || + (! json_is_array (root))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + num = json_array_size (root); + if (32 <= num || 0 == num) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + age_commitment->num = num; + age_commitment->keys = + GNUNET_new_array (num, + struct TALER_AgeCommitmentPublicKeyP); + + json_array_foreach (root, idx, pk) { + const char *emsg; + unsigned int eline; + struct GNUNET_JSON_Specification pkspec[] = { + GNUNET_JSON_spec_fixed_auto ( + NULL, + &age_commitment->keys[idx].pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (pk, + pkspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + }; + + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing age commitment + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_age_commitment (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_AgeCommitment *age_commitment = spec->ptr; + + (void) cls; + + if (NULL == age_commitment || + NULL == age_commitment->keys) + return; + + age_commitment->num = 0; + GNUNET_free (age_commitment->keys); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_age_commitment (const char *name, + struct TALER_AgeCommitment *age_commitment) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_age_commitment, + .cleaner = &clean_age_commitment, + .field = name, + .ptr = age_commitment + }; + + return ret; +} + + +/** + * Parse given JSON object to denomination public key. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_denom_pub (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationPublicKey *denom_pub = spec->ptr; + struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub; + const char *cipher; + bool age_mask_missing = false; + struct GNUNET_JSON_Specification dspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("age_mask", + &denom_pub->age_mask.bits), + &age_mask_missing), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + (void) cls; + if (GNUNET_OK != + GNUNET_JSON_parse (root, + dspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (age_mask_missing) + denom_pub->age_mask.bits = 0; + bsign_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bsign_pub->rc = 1; + bsign_pub->cipher = string_to_cipher (cipher); + switch (bsign_pub->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_rsa_public_key ( + "rsa_public_key", + &bsign_pub->details.rsa_public_key), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; + } + denom_pub->bsign_pub_key = bsign_pub; + return GNUNET_OK; + } + case GNUNET_CRYPTO_BSA_CS: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_fixed ("cs_public_key", + &bsign_pub->details.cs_public_key, + sizeof (bsign_pub->details.cs_public_key)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; + } + denom_pub->bsign_pub_key = bsign_pub; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; +} + + +/** + * Cleanup data left from parsing denomination public key. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_denom_pub (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationPublicKey *denom_pub = spec->ptr; + + (void) cls; + TALER_denom_pub_free (denom_pub); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_denom_pub (const char *field, + struct TALER_DenominationPublicKey *pk) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_denom_pub, + .cleaner = &clean_denom_pub, + .field = field, + .ptr = pk + }; + + pk->bsign_pub_key = NULL; + return ret; +} + + +/** + * Parse given JSON object partially into a denomination public key. + * + * Depending on the cipher in cls, it parses the corresponding public key type. + * + * @param cls closure, enum GNUNET_CRYPTO_BlindSignatureAlgorithm + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_denom_pub_cipher (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationPublicKey *denom_pub = spec->ptr; + enum GNUNET_CRYPTO_BlindSignatureAlgorithm cipher = + (enum GNUNET_CRYPTO_BlindSignatureAlgorithm) (long) cls; + struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub; + const char *emsg; + unsigned int eline; + + bsign_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bsign_pub->cipher = cipher; + bsign_pub->rc = 1; + switch (cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_rsa_public_key ( + "rsa_pub", + &bsign_pub->details.rsa_public_key), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; + } + denom_pub->bsign_pub_key = bsign_pub; + return GNUNET_OK; + } + case GNUNET_CRYPTO_BSA_CS: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_fixed ("cs_pub", + &bsign_pub->details.cs_public_key, + sizeof (bsign_pub->details.cs_public_key)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; + } + denom_pub->bsign_pub_key = bsign_pub; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_denom_pub_cipher (const char *field, + enum GNUNET_CRYPTO_BlindSignatureAlgorithm + cipher, + struct TALER_DenominationPublicKey *pk) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_denom_pub_cipher, + .cleaner = &clean_denom_pub, + .field = field, + .cls = (void *) cipher, + .ptr = pk + }; + + return ret; +} + + +/** + * Parse given JSON object to denomination signature. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_denom_sig (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationSignature *denom_sig = spec->ptr; + struct GNUNET_CRYPTO_UnblindedSignature *unblinded_sig; + const char *cipher; + struct GNUNET_JSON_Specification dspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + (void) cls; + if (GNUNET_OK != + GNUNET_JSON_parse (root, + dspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + unblinded_sig = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature); + unblinded_sig->cipher = string_to_cipher (cipher); + unblinded_sig->rc = 1; + switch (unblinded_sig->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_rsa_signature ( + "rsa_signature", + &unblinded_sig->details.rsa_signature), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (unblinded_sig); + return GNUNET_SYSERR; + } + denom_sig->unblinded_sig = unblinded_sig; + return GNUNET_OK; + } + case GNUNET_CRYPTO_BSA_CS: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_fixed_auto ("cs_signature_r", + &unblinded_sig->details.cs_signature. + r_point), + GNUNET_JSON_spec_fixed_auto ("cs_signature_s", + &unblinded_sig->details.cs_signature. + s_scalar), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (unblinded_sig); + return GNUNET_SYSERR; + } + denom_sig->unblinded_sig = unblinded_sig; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + GNUNET_free (unblinded_sig); + return GNUNET_SYSERR; +} + + +/** + * Cleanup data left from parsing denomination public key. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_denom_sig (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_DenominationSignature *denom_sig = spec->ptr; + + (void) cls; + TALER_denom_sig_free (denom_sig); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_denom_sig (const char *field, + struct TALER_DenominationSignature *sig) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_denom_sig, + .cleaner = &clean_denom_sig, + .field = field, + .ptr = sig + }; + + sig->unblinded_sig = NULL; + return ret; +} + + +/** + * Parse given JSON object to blinded denomination signature. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_blinded_denom_sig (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_BlindedDenominationSignature *denom_sig = spec->ptr; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; + const char *cipher; + struct GNUNET_JSON_Specification dspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + (void) cls; + if (GNUNET_OK != + GNUNET_JSON_parse (root, + dspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blinded_sig->cipher = string_to_cipher (cipher); + blinded_sig->rc = 1; + switch (blinded_sig->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_rsa_signature ( + "blinded_rsa_signature", + &blinded_sig->details.blinded_rsa_signature), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (blinded_sig); + return GNUNET_SYSERR; + } + denom_sig->blinded_sig = blinded_sig; + return GNUNET_OK; + } + case GNUNET_CRYPTO_BSA_CS: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_uint32 ("b", + &blinded_sig->details.blinded_cs_answer.b), + GNUNET_JSON_spec_fixed_auto ("s", + &blinded_sig->details.blinded_cs_answer. + s_scalar), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (blinded_sig); + return GNUNET_SYSERR; + } + denom_sig->blinded_sig = blinded_sig; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + GNUNET_free (blinded_sig); + return GNUNET_SYSERR; +} + + +/** + * Cleanup data left from parsing denomination public key. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_blinded_denom_sig (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_BlindedDenominationSignature *denom_sig = spec->ptr; + + (void) cls; + TALER_blinded_denom_sig_free (denom_sig); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_blinded_denom_sig ( + const char *field, + struct TALER_BlindedDenominationSignature *sig) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_blinded_denom_sig, + .cleaner = &clean_blinded_denom_sig, + .field = field, + .ptr = sig + }; + + sig->blinded_sig = NULL; + return ret; +} + + +/** + * Parse given JSON object to blinded planchet. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_blinded_planchet (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_BlindedPlanchet *blinded_planchet = spec->ptr; + struct GNUNET_CRYPTO_BlindedMessage *blinded_message; + const char *cipher; + struct GNUNET_JSON_Specification dspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + + (void) cls; + if (GNUNET_OK != + GNUNET_JSON_parse (root, + dspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage); + blinded_message->rc = 1; + blinded_message->cipher = string_to_cipher (cipher); + switch (blinded_message->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_varsize ( + "rsa_blinded_planchet", + &blinded_message->details.rsa_blinded_message.blinded_msg, + &blinded_message->details.rsa_blinded_message.blinded_msg_size), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (blinded_message); + return GNUNET_SYSERR; + } + blinded_planchet->blinded_message = blinded_message; + return GNUNET_OK; + } + case GNUNET_CRYPTO_BSA_CS: + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_fixed_auto ( + "cs_nonce", + &blinded_message->details.cs_blinded_message.nonce), + GNUNET_JSON_spec_fixed_auto ( + "cs_blinded_c0", + &blinded_message->details.cs_blinded_message.c[0]), + GNUNET_JSON_spec_fixed_auto ( + "cs_blinded_c1", + &blinded_message->details.cs_blinded_message.c[1]), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (blinded_message); + return GNUNET_SYSERR; + } + blinded_planchet->blinded_message = blinded_message; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + GNUNET_free (blinded_message); + return GNUNET_SYSERR; +} + + +/** + * Cleanup data left from parsing blinded planchet. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_blinded_planchet (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_BlindedPlanchet *blinded_planchet = spec->ptr; + + (void) cls; + TALER_blinded_planchet_free (blinded_planchet); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_blinded_planchet (const char *field, + struct TALER_BlindedPlanchet *blinded_planchet) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_blinded_planchet, + .cleaner = &clean_blinded_planchet, + .field = field, + .ptr = blinded_planchet + }; + + blinded_planchet->blinded_message = NULL; + return ret; +} + + +/** + * Parse given JSON object to exchange withdraw values (/csr). + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_exchange_withdraw_values (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_ExchangeWithdrawValues *ewv = spec->ptr; + struct GNUNET_CRYPTO_BlindingInputValues *bi; + const char *cipher; + struct GNUNET_JSON_Specification dspec[] = { + GNUNET_JSON_spec_string ("cipher", + &cipher), + GNUNET_JSON_spec_end () + }; + const char *emsg; + unsigned int eline; + enum GNUNET_CRYPTO_BlindSignatureAlgorithm ci; + + (void) cls; + if (GNUNET_OK != + GNUNET_JSON_parse (root, + dspec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ci = string_to_cipher (cipher); + switch (ci) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ewv->blinding_inputs = TALER_denom_ewv_rsa_singleton ()->blinding_inputs; + return GNUNET_OK; + case GNUNET_CRYPTO_BSA_CS: + bi = GNUNET_new (struct GNUNET_CRYPTO_BlindingInputValues); + bi->cipher = GNUNET_CRYPTO_BSA_CS; + bi->rc = 1; + { + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_fixed ( + "r_pub_0", + &bi->details.cs_values.r_pub[0], + sizeof (struct GNUNET_CRYPTO_CsRPublic)), + GNUNET_JSON_spec_fixed ( + "r_pub_1", + &bi->details.cs_values.r_pub[1], + sizeof (struct GNUNET_CRYPTO_CsRPublic)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (root, + ispec, + &emsg, + &eline)) + { + GNUNET_break_op (0); + GNUNET_free (bi); + return GNUNET_SYSERR; + } + ewv->blinding_inputs = bi; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + return GNUNET_SYSERR; +} + + +/** + * Cleanup data left from parsing withdraw values + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_exchange_withdraw_values ( + void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_ExchangeWithdrawValues *ewv = spec->ptr; + + (void) cls; + TALER_denom_ewv_free (ewv); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_exchange_withdraw_values ( + const char *field, + struct TALER_ExchangeWithdrawValues *ewv) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_exchange_withdraw_values, + .cleaner = &clean_exchange_withdraw_values, + .field = field, + .ptr = ewv + }; + + ewv->blinding_inputs = NULL; + return ret; +} + + +/** + * Closure for #parse_i18n_string. + */ +struct I18nContext +{ + /** + * Language pattern to match. + */ + char *lp; + + /** + * Name of the field to match. + */ + const char *field; +}; + + +/** + * Parse given JSON object to internationalized string. + * + * @param cls closure, our `struct I18nContext *` + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_i18n_string (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct I18nContext *ctx = cls; + json_t *i18n; + json_t *val; + + { + char *i18nf; + + GNUNET_asprintf (&i18nf, + "%s_i18n", + ctx->field); + i18n = json_object_get (root, + i18nf); + GNUNET_free (i18nf); + } + + val = json_object_get (root, + ctx->field); + if ( (NULL != i18n) && + (NULL != ctx->lp) ) + { + double best = 0.0; + json_t *pos; + const char *lang; + + json_object_foreach (i18n, lang, pos) + { + double score; + + score = TALER_language_matches (ctx->lp, + lang); + if (score > best) + { + best = score; + val = pos; + } + } + } + + { + const char *str; + + str = json_string_value (val); + *(const char **) spec->ptr = str; + } + return GNUNET_OK; +} + + +/** + * Function called to clean up data from earlier parsing. + * + * @param cls closure + * @param spec our specification entry with data to clean. + */ +static void +i18n_cleaner (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct I18nContext *ctx = cls; + + (void) spec; + if (NULL != ctx) + { + GNUNET_free (ctx->lp); + GNUNET_free (ctx); + } +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_i18n_string (const char *name, + const char *language_pattern, + const char **strptr) +{ + struct I18nContext *ctx = GNUNET_new (struct I18nContext); + struct GNUNET_JSON_Specification ret = { + .parser = &parse_i18n_string, + .cleaner = &i18n_cleaner, + .cls = ctx, + .field = NULL, /* we want the main object */ + .ptr = strptr, + .ptr_size = 0, + .size_ptr = NULL + }; + + ctx->lp = (NULL != language_pattern) + ? GNUNET_strdup (language_pattern) + : NULL; + ctx->field = name; + *strptr = NULL; + return ret; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_i18n_str (const char *name, + const char **strptr) +{ + const char *lang = getenv ("LANG"); + char *dot; + char *l; + struct GNUNET_JSON_Specification ret; + + if (NULL != lang) + { + dot = strchr (lang, + '.'); + if (NULL == dot) + l = GNUNET_strdup (lang); + else + l = GNUNET_strndup (lang, + dot - lang); + } + else + { + l = NULL; + } + ret = TALER_JSON_spec_i18n_string (name, + l, + strptr); + GNUNET_free (l); + return ret; +} + + +/** + * Parse given JSON object with Taler error code. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_ec (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + enum TALER_ErrorCode *ec = spec->ptr; + json_int_t num; + + (void) cls; + if (! json_is_integer (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + num = json_integer_value (root); + if (num < 0) + { + GNUNET_break_op (0); + *ec = TALER_EC_INVALID; + return GNUNET_SYSERR; + } + *ec = (enum TALER_ErrorCode) num; + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_ec (const char *field, + enum TALER_ErrorCode *ec) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_ec, + .field = field, + .ptr = ec + }; + + *ec = TALER_EC_NONE; + return ret; +} + + +/** + * Parse given JSON object to web URL. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_web_url (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *str; + + (void) cls; + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (! TALER_is_web_url (str)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + *(const char **) spec->ptr = str; + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_web_url (const char *field, + const char **url) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_web_url, + .field = field, + .ptr = url + }; + + *url = NULL; + return ret; +} + + +/** + * Parse given JSON object to payto:// URI. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_payto_uri (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *str; + char *err; + + (void) cls; + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + err = TALER_payto_validate (str); + if (NULL != err) + { + GNUNET_break_op (0); + GNUNET_free (err); + return GNUNET_SYSERR; + } + *(const char **) spec->ptr = str; + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_payto_uri (const char *field, + const char **payto_uri) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_payto_uri, + .field = field, + .ptr = payto_uri + }; + + *payto_uri = NULL; + return ret; +} + + +/** + * Parse given JSON object with protocol version. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_protocol_version (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct TALER_JSON_ProtocolVersion *pv = spec->ptr; + const char *ver; + char dummy; + + (void) cls; + if (! json_is_string (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ver = json_string_value (root); + if (3 != sscanf (ver, + "%u:%u:%u%c", + &pv->current, + &pv->revision, + &pv->age, + &dummy)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_version (const char *field, + struct TALER_JSON_ProtocolVersion *ver) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_protocol_version, + .field = field, + .ptr = ver + }; + + return ret; +} + + +/* end of json/json_helper.c */ diff --git a/src/json/json_pack.c b/src/json/json_pack.c @@ -0,0 +1,324 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file json/json_pack.c + * @brief helper functions for JSON object packing + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler/taler_util.h" +#include "taler/taler_json_lib.h" + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_time_abs_human (const char *name, + struct GNUNET_TIME_Absolute at) +{ + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + .object = json_string ( + GNUNET_STRINGS_absolute_time_to_string (at)) + }; + + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_econtract ( + const char *name, + const struct TALER_EncryptedContract *econtract) +{ + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == econtract) + return ps; + ps.object + = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_varsize ("econtract", + econtract->econtract, + econtract->econtract_size), + GNUNET_JSON_pack_data_auto ("econtract_sig", + &econtract->econtract_sig), + GNUNET_JSON_pack_data_auto ("contract_pub", + &econtract->contract_pub)); + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_age_commitment ( + const char *name, + const struct TALER_AgeCommitment *age_commitment) +{ + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + json_t *keys; + + if (NULL == age_commitment || + 0 == age_commitment->num) + return ps; + + GNUNET_assert (NULL != + (keys = json_array ())); + + for (size_t i = 0; + i < age_commitment->num; + i++) + { + json_t *val; + val = GNUNET_JSON_from_data (&age_commitment->keys[i], + sizeof(age_commitment->keys[i])); + GNUNET_assert (NULL != val); + GNUNET_assert (0 == + json_array_append_new (keys, val)); + } + + ps.object = keys; + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_denom_pub ( + const char *name, + const struct TALER_DenominationPublicKey *pk) +{ + const struct GNUNET_CRYPTO_BlindSignPublicKey *bsp; + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == pk) + return ps; + bsp = pk->bsign_pub_key; + switch (bsp->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ps.object + = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "RSA"), + GNUNET_JSON_pack_uint64 ("age_mask", + pk->age_mask.bits), + GNUNET_JSON_pack_rsa_public_key ("rsa_public_key", + bsp->details.rsa_public_key)); + return ps; + case GNUNET_CRYPTO_BSA_CS: + ps.object + = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "CS"), + GNUNET_JSON_pack_uint64 ("age_mask", + pk->age_mask.bits), + GNUNET_JSON_pack_data_varsize ("cs_public_key", + &bsp->details.cs_public_key, + sizeof (bsp->details.cs_public_key))); + return ps; + } + GNUNET_assert (0); + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_denom_sig ( + const char *name, + const struct TALER_DenominationSignature *sig) +{ + const struct GNUNET_CRYPTO_UnblindedSignature *bs; + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == sig) + return ps; + bs = sig->unblinded_sig; + switch (bs->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "RSA"), + GNUNET_JSON_pack_rsa_signature ("rsa_signature", + bs->details.rsa_signature)); + return ps; + case GNUNET_CRYPTO_BSA_CS: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "CS"), + GNUNET_JSON_pack_data_auto ("cs_signature_r", + &bs->details.cs_signature.r_point), + GNUNET_JSON_pack_data_auto ("cs_signature_s", + &bs->details.cs_signature.s_scalar)); + return ps; + } + GNUNET_assert (0); + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_exchange_withdraw_values ( + const char *name, + const struct TALER_ExchangeWithdrawValues *ewv) +{ + const struct GNUNET_CRYPTO_BlindingInputValues *biv; + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == ewv) + return ps; + biv = ewv->blinding_inputs; + switch (biv->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "RSA")); + return ps; + case GNUNET_CRYPTO_BSA_CS: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "CS"), + GNUNET_JSON_pack_data_varsize ( + "r_pub_0", + &biv->details.cs_values.r_pub[0], + sizeof(struct GNUNET_CRYPTO_CsRPublic)), + GNUNET_JSON_pack_data_varsize ( + "r_pub_1", + &biv->details.cs_values.r_pub[1], + sizeof(struct GNUNET_CRYPTO_CsRPublic)) + ); + return ps; + } + GNUNET_assert (0); + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_blinded_denom_sig ( + const char *name, + const struct TALER_BlindedDenominationSignature *sig) +{ + const struct GNUNET_CRYPTO_BlindedSignature *bs; + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == sig) + return ps; + bs = sig->blinded_sig; + switch (bs->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "RSA"), + GNUNET_JSON_pack_rsa_signature ("blinded_rsa_signature", + bs->details.blinded_rsa_signature)); + return ps; + case GNUNET_CRYPTO_BSA_CS: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "CS"), + GNUNET_JSON_pack_uint64 ("b", + bs->details.blinded_cs_answer.b), + GNUNET_JSON_pack_data_auto ("s", + &bs->details.blinded_cs_answer.s_scalar)); + return ps; + } + GNUNET_assert (0); + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_blinded_planchet ( + const char *name, + const struct TALER_BlindedPlanchet *blinded_planchet) +{ + const struct GNUNET_CRYPTO_BlindedMessage *bm; + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == blinded_planchet) + return ps; + bm = blinded_planchet->blinded_message; + switch (bm->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "RSA"), + GNUNET_JSON_pack_data_varsize ( + "rsa_blinded_planchet", + bm->details.rsa_blinded_message.blinded_msg, + bm->details.rsa_blinded_message.blinded_msg_size)); + return ps; + case GNUNET_CRYPTO_BSA_CS: + ps.object = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + "CS"), + GNUNET_JSON_pack_data_auto ( + "cs_nonce", + &bm->details.cs_blinded_message.nonce), + GNUNET_JSON_pack_data_auto ( + "cs_blinded_c0", + &bm->details.cs_blinded_message.c[0]), + GNUNET_JSON_pack_data_auto ( + "cs_blinded_c1", + &bm->details.cs_blinded_message.c[1])); + return ps; + } + GNUNET_assert (0); + return ps; +} + + +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_amount (const char *name, + const struct TALER_Amount *amount) +{ + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + .object = (NULL != amount) + ? TALER_JSON_from_amount (amount) + : NULL + }; + + return ps; +} + + +/* End of json/json_pack.c */