diff options
Diffstat (limited to 'src/json')
-rw-r--r-- | src/json/Makefile.am | 18 | ||||
-rw-r--r-- | src/json/json.c | 243 | ||||
-rw-r--r-- | src/json/json_helper.c | 1331 | ||||
-rw-r--r-- | src/json/json_pack.c | 248 | ||||
-rw-r--r-- | src/json/json_wire.c | 78 | ||||
-rw-r--r-- | src/json/test_json.c | 68 | ||||
-rw-r--r-- | src/json/test_json_wire.c | 80 |
7 files changed, 1560 insertions, 506 deletions
diff --git a/src/json/Makefile.am b/src/json/Makefile.am index 2f5ec3f17..ce863cb7e 100644 --- a/src/json/Makefile.am +++ b/src/json/Makefile.am @@ -16,7 +16,7 @@ libtalerjson_la_SOURCES = \ json_pack.c \ json_wire.c libtalerjson_la_LDFLAGS = \ - -version-info 1:0:1 \ + -version-info 3:0:1 \ -no-undefined libtalerjson_la_LIBADD = \ $(top_builddir)/src/util/libtalerutil.la \ @@ -28,12 +28,10 @@ libtalerjson_la_LIBADD = \ $(XLIB) TESTS = \ - test_json \ - test_json_wire + test_json check_PROGRAMS= \ - test_json \ - test_json_wire + test_json test_json_SOURCES = \ test_json.c @@ -43,13 +41,3 @@ test_json_LDADD = \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil \ -ljansson - - -test_json_wire_SOURCES = \ - test_json_wire.c -test_json_wire_LDADD = \ - $(top_builddir)/src/json/libtalerjson.la \ - -lgnunetjson \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil \ - -ljansson diff --git a/src/json/json.c b/src/json/json.c index 956aad1a5..639bd530c 100644 --- a/src/json/json.c +++ b/src/json/json.c @@ -62,161 +62,6 @@ contains_real (const json_t *json) /** - * Dump character in the low range into @a buf - * following RFC 8785. - * - * @param[in,out] buf buffer to modify - * @param val value to dump - */ -static void -lowdump (struct GNUNET_Buffer *buf, - unsigned char val) -{ - char scratch[7]; - - switch (val) - { - case 0x8: - GNUNET_buffer_write (buf, - "\b", - 2); - break; - case 0x9: - GNUNET_buffer_write (buf, - "\t", - 2); - break; - case 0xA: - GNUNET_buffer_write (buf, - "\n", - 2); - break; - case 0xC: - GNUNET_buffer_write (buf, - "\f", - 2); - break; - case 0xD: - GNUNET_buffer_write (buf, - "\r", - 2); - break; - default: - GNUNET_snprintf (scratch, - sizeof (scratch), - "\\u%04x", - (unsigned int) val); - GNUNET_buffer_write (buf, - scratch, - 6); - break; - } -} - - -/** - * Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2). - * - * @param[in,out] inp pointer to string to re-encode - * @return number of bytes in resulting @a inp - */ -static size_t -rfc8785encode (char **inp) -{ - struct GNUNET_Buffer buf = { 0 }; - size_t left = strlen (*inp) + 1; - size_t olen; - char *in = *inp; - const char *pos = in; - - GNUNET_buffer_prealloc (&buf, - left + 40); - buf.warn_grow = 0; /* disable, + 40 is just a wild guess */ - while (1) - { - int mbl = u8_mblen ((unsigned char *) pos, - left); - unsigned char val; - - if (0 == mbl) - break; - val = (unsigned char) *pos; - if ( (1 == mbl) && - (val <= 0x1F) ) - { - lowdump (&buf, - val); - } - else if ( (1 == mbl) && ('\\' == *pos) ) - { - switch (*(pos + 1)) - { - case '\\': - mbl = 2; - GNUNET_buffer_write (&buf, - pos, - mbl); - break; - case 'u': - { - unsigned int num; - uint32_t n32; - unsigned char res[8]; - size_t rlen; - - GNUNET_assert ( (1 == - sscanf (pos + 2, - "%4x", - &num)) || - (1 == - sscanf (pos + 2, - "%4X", - &num)) ); - mbl = 6; - n32 = (uint32_t) num; - rlen = sizeof (res); - u32_to_u8 (&n32, - 1, - res, - &rlen); - if ( (1 == rlen) && - (res[0] <= 0x1F) ) - { - lowdump (&buf, - res[0]); - } - else - { - GNUNET_buffer_write (&buf, - (const char *) res, - rlen); - } - } - break; - } - } - else - { - GNUNET_buffer_write (&buf, - pos, - mbl); - } - left -= mbl; - pos += mbl; - } - - /* 0-terminate buffer */ - GNUNET_buffer_write (&buf, - "", - 1); - GNUNET_free (in); - *inp = GNUNET_buffer_reap (&buf, - &olen); - return olen; -} - - -/** * Dump the @a json to a string and hash it. * * @param json value to hash @@ -253,7 +98,7 @@ dump_and_hash (const json_t *json, GNUNET_break (0); return GNUNET_SYSERR; } - len = rfc8785encode (&wire_enc); + len = TALER_rfc8785encode (&wire_enc); if (NULL == salt) { GNUNET_CRYPTO_hash (wire_enc, @@ -481,7 +326,7 @@ forget (const json_t *in, enum GNUNET_GenericReturnValue TALER_JSON_contract_hash (const json_t *json, - struct TALER_PrivateContractHash *hc) + struct TALER_PrivateContractHashP *hc) { enum GNUNET_GenericReturnValue ret; json_t *cjson; @@ -688,7 +533,7 @@ TALER_JSON_contract_part_forget (json_t *json, /** - * Look over all of the values of a '$forgettable' object. Replace 'True' + * Loop over all of the values of a '$forgettable' object. Replace 'True' * values with proper random salts. Fails if any forgettable values are * neither 'True' nor valid salts (strings). * @@ -733,51 +578,64 @@ seed_forgettable (json_t *f) } -/** - * Take a given contract with "forgettable" fields marked - * but with 'True' instead of a real salt. Replaces all - * 'True' values with proper random salts. Fails if any - * forgettable markers are neither 'True' nor valid salts. - * - * @param[in,out] json JSON to transform - * @return #GNUNET_OK on success - */ enum GNUNET_GenericReturnValue -TALER_JSON_contract_seed_forgettable (json_t *json) +TALER_JSON_contract_seed_forgettable (const json_t *spec, + json_t *contract) { - if (json_is_object (json)) + if (json_is_object (spec)) { const char *key; json_t *val; - json_object_foreach (json, + json_object_foreach ((json_t *) spec, key, val) { + json_t *cval = json_object_get (contract, + key); + if (0 == strcmp ("$forgettable", key)) { + json_t *xval = json_deep_copy (val); + if (GNUNET_OK != - seed_forgettable (val)) + seed_forgettable (xval)) + { + json_decref (xval); return GNUNET_SYSERR; + } + GNUNET_assert (0 == + json_object_set_new (contract, + "$forgettable", + xval)); continue; } + if (NULL == cval) + continue; if (GNUNET_OK != - TALER_JSON_contract_seed_forgettable (val)) + TALER_JSON_contract_seed_forgettable (val, + cval)) return GNUNET_SYSERR; } } - if (json_is_array (json)) + if (json_is_array (spec)) { size_t index; json_t *val; - json_array_foreach (json, + json_array_foreach ((json_t *) spec, index, val) { + json_t *ival = json_array_get (contract, + index); + + if (NULL == ival) + continue; if (GNUNET_OK != - TALER_JSON_contract_seed_forgettable (val)) + TALER_JSON_contract_seed_forgettable (val, + ival)) return GNUNET_SYSERR; } } @@ -810,6 +668,7 @@ parse_path (json_t *obj, json_t *next_obj = NULL; char *next_dot; + GNUNET_assert (NULL != id); /* make stupid compiler happy */ if (NULL == next_id) { cb (cb_cls, @@ -999,22 +858,40 @@ TALER_JSON_get_error_code2 (const void *data, void -TALER_deposit_extension_hash (const json_t *extensions, - struct TALER_ExtensionContractHash *ech) +TALER_deposit_policy_hash (const json_t *policy, + struct TALER_ExtensionPolicyHashP *ech) { GNUNET_assert (GNUNET_OK == - dump_and_hash (extensions, - "taler-contract-extensions", + 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_extension_config_hash (const json_t *config, - struct TALER_ExtensionConfigHash *ech) +TALER_JSON_extensions_manifests_hash (const json_t *manifests, + struct TALER_ExtensionManifestsHashP *ech) { - return dump_and_hash (config, - "taler-extension-configuration", + return dump_and_hash (manifests, + "taler-extensions-manifests", &ech->hash); } diff --git a/src/json/json_helper.c b/src/json/json_helper.c index 3b4da5595..0a533610b 100644 --- a/src/json/json_helper.c +++ b/src/json/json_helper.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2023 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 @@ -26,6 +26,29 @@ #include "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) { @@ -41,17 +64,6 @@ TALER_JSON_from_amount (const struct TALER_Amount *amount) } -json_t * -TALER_JSON_from_amount_nbo (const struct TALER_AmountNBO *amount) -{ - struct TALER_Amount a; - - TALER_amount_ntoh (&a, - amount); - return TALER_JSON_from_amount (&a); -} - - /** * Parse given JSON object to Amount * @@ -87,6 +99,11 @@ parse_amount (void *cls, 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; @@ -132,7 +149,7 @@ TALER_JSON_spec_amount_any (const char *name, /** - * Parse given JSON object to Amount in NBO. + * Parse given JSON object to currency spec. * * @param cls closure, NULL * @param root the json object representing data @@ -140,75 +157,375 @@ TALER_JSON_spec_amount_any (const char *name, * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error */ static enum GNUNET_GenericReturnValue -parse_amount_nbo (void *cls, - json_t *root, - struct GNUNET_JSON_Specification *spec) +parse_cspec (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) { - const char *currency = cls; - struct TALER_AmountNBO *r_amount = spec->ptr; - const char *sv; + 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; - (void) cls; - if (! json_is_string (root)) + 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 (0); + GNUNET_break_op (0); return GNUNET_SYSERR; } - sv = json_string_value (root); if (GNUNET_OK != - TALER_string_to_amount_nbo (sv, - r_amount)) + TALER_check_currency (currency)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "`%s' is not a valid amount\n", - sv); GNUNET_break_op (0); return GNUNET_SYSERR; } - if ( (NULL != currency) && - (0 != - strcasecmp (currency, - r_amount->currency)) ) + 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_amount_nbo (const char *name, - const char *currency, - struct TALER_AmountNBO *r_amount) +TALER_JSON_spec_currency_specification ( + const char *name, + const char *currency, + struct TALER_CurrencySpecification *r_cspec) { struct GNUNET_JSON_Specification ret = { - .parser = &parse_amount_nbo, - .cleaner = NULL, + .parser = &parse_cspec, + .cleaner = &clean_cspec, .cls = (void *) currency, .field = name, - .ptr = r_amount, - .ptr_size = 0, + .ptr = r_cspec, + .ptr_size = sizeof (*r_cspec), .size_ptr = NULL }; - GNUNET_assert (NULL != currency); + 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_amount_any_nbo (const char *name, - struct TALER_AmountNBO *r_amount) +TALER_JSON_spec_denomination_group (const char *name, + const char *currency, + struct TALER_DenominationGroup *group) { struct GNUNET_JSON_Specification ret = { - .parser = &parse_amount_nbo, - .cleaner = NULL, - .cls = NULL, + .cls = (void *) currency, + .parser = &parse_denomination_group, .field = name, - .ptr = r_amount, - .ptr_size = 0, - .size_ptr = NULL + .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; @@ -229,12 +546,16 @@ parse_denom_pub (void *cls, struct GNUNET_JSON_Specification *spec) { struct TALER_DenominationPublicKey *denom_pub = spec->ptr; - uint32_t cipher; + struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub; + const char *cipher; + bool age_mask_missing = false; struct GNUNET_JSON_Specification dspec[] = { - GNUNET_JSON_spec_uint32 ("cipher", + GNUNET_JSON_spec_string ("cipher", &cipher), - GNUNET_JSON_spec_uint32 ("age_mask", - &denom_pub->age_mask.mask), + 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; @@ -250,15 +571,22 @@ parse_denom_pub (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - denom_pub->cipher = (enum TALER_DenominationCipher) cipher; - switch (denom_pub->cipher) + + 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 TALER_DENOMINATION_RSA: + 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", - &denom_pub->details.rsa_public_key), + &bsign_pub->details.rsa_public_key), GNUNET_JSON_spec_end () }; @@ -269,14 +597,38 @@ parse_denom_pub (void *cls, &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; } - default: - GNUNET_break_op (0); - return GNUNET_SYSERR; } + GNUNET_break_op (0); + GNUNET_free (bsign_pub); + return GNUNET_SYSERR; } @@ -308,6 +660,105 @@ TALER_JSON_spec_denom_pub (const char *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; } @@ -326,9 +777,10 @@ parse_denom_sig (void *cls, struct GNUNET_JSON_Specification *spec) { struct TALER_DenominationSignature *denom_sig = spec->ptr; - uint32_t cipher; + struct GNUNET_CRYPTO_UnblindedSignature *unblinded_sig; + const char *cipher; struct GNUNET_JSON_Specification dspec[] = { - GNUNET_JSON_spec_uint32 ("cipher", + GNUNET_JSON_spec_string ("cipher", &cipher), GNUNET_JSON_spec_end () }; @@ -345,15 +797,19 @@ parse_denom_sig (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - denom_sig->cipher = (enum TALER_DenominationCipher) cipher; - switch (denom_sig->cipher) + unblinded_sig = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature); + unblinded_sig->cipher = string_to_cipher (cipher); + unblinded_sig->rc = 1; + switch (unblinded_sig->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: { struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_rsa_signature ( "rsa_signature", - &denom_sig->details.rsa_signature), + &unblinded_sig->details.rsa_signature), GNUNET_JSON_spec_end () }; @@ -364,14 +820,41 @@ parse_denom_sig (void *cls, &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; } - default: - GNUNET_break_op (0); - return GNUNET_SYSERR; } + GNUNET_break_op (0); + GNUNET_free (unblinded_sig); + return GNUNET_SYSERR; } @@ -403,6 +886,7 @@ TALER_JSON_spec_denom_sig (const char *field, .ptr = sig }; + sig->unblinded_sig = NULL; return ret; } @@ -421,9 +905,10 @@ parse_blinded_denom_sig (void *cls, struct GNUNET_JSON_Specification *spec) { struct TALER_BlindedDenominationSignature *denom_sig = spec->ptr; - uint32_t cipher; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; + const char *cipher; struct GNUNET_JSON_Specification dspec[] = { - GNUNET_JSON_spec_uint32 ("cipher", + GNUNET_JSON_spec_string ("cipher", &cipher), GNUNET_JSON_spec_end () }; @@ -440,15 +925,19 @@ parse_blinded_denom_sig (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - denom_sig->cipher = (enum TALER_DenominationCipher) cipher; - switch (denom_sig->cipher) + blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blinded_sig->cipher = string_to_cipher (cipher); + blinded_sig->rc = 1; + switch (blinded_sig->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: { struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_rsa_signature ( "blinded_rsa_signature", - &denom_sig->details.blinded_rsa_signature), + &blinded_sig->details.blinded_rsa_signature), GNUNET_JSON_spec_end () }; @@ -459,14 +948,40 @@ parse_blinded_denom_sig (void *cls, &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; } - default: - GNUNET_break_op (0); - return GNUNET_SYSERR; } + GNUNET_break_op (0); + GNUNET_free (blinded_sig); + return GNUNET_SYSERR; } @@ -499,6 +1014,253 @@ TALER_JSON_spec_blinded_denom_sig ( .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; } @@ -575,11 +1337,6 @@ parse_i18n_string (void *cls, const char *str; str = json_string_value (val); - if (NULL == str) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } *(const char **) spec->ptr = str; } return GNUNET_OK; @@ -599,8 +1356,11 @@ i18n_cleaner (void *cls, struct I18nContext *ctx = cls; (void) spec; - GNUNET_free (ctx->lp); - GNUNET_free (ctx); + if (NULL != ctx) + { + GNUNET_free (ctx->lp); + GNUNET_free (ctx); + } } @@ -620,8 +1380,9 @@ TALER_JSON_spec_i18n_string (const char *name, .size_ptr = NULL }; - ctx->lp = (NULL != language_pattern) ? GNUNET_strdup (language_pattern) : - NULL; + ctx->lp = (NULL != language_pattern) + ? GNUNET_strdup (language_pattern) + : NULL; ctx->field = name; *strptr = NULL; return ret; @@ -659,36 +1420,400 @@ TALER_JSON_spec_i18n_str (const char *name, } -enum GNUNET_GenericReturnValue -TALER_agemask_parse_json (const json_t *root, - struct TALER_AgeMask *mask) +/** + * 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) { - const char *name; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("extension", - &name), - GNUNET_JSON_spec_uint32 ("mask", - &mask->mask), - GNUNET_JSON_spec_end () + 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 }; - if (GNUNET_OK != GNUNET_JSON_parse (root, - spec, - NULL, - NULL)) + *ec = TALER_EC_NONE; + return ret; +} + + +/** + * Parse given JSON object with AML decision. + * + * @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_aml_decision (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + enum TALER_AmlDecisionState *aml = 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 > TALER_AML_MAX) || + (num < 0) ) + { + GNUNET_break_op (0); return GNUNET_SYSERR; } + *aml = (enum TALER_AmlDecisionState) num; + return GNUNET_OK; +} + - if (! strncmp (name, - "age_restriction", - sizeof("age_restriction"))) +struct GNUNET_JSON_Specification +TALER_JSON_spec_aml_decision (const char *field, + enum TALER_AmlDecisionState *aml_state) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_aml_decision, + .field = field, + .ptr = aml_state + }; + + 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; +} + + +/** + * Parse given JSON object to an OTP 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_otp_key (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *pos_key; + + (void) cls; + pos_key = json_string_value (root); + if (NULL == pos_key) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + { + size_t pos_key_length = strlen (pos_key); + void *key; /* pos_key in binary */ + size_t key_len; /* length of the key */ + int dret; + + key_len = pos_key_length * 5 / 8; + key = GNUNET_malloc (key_len); + dret = TALER_rfc3548_base32decode (pos_key, + pos_key_length, + key, + key_len); + if (-1 == dret) + { + GNUNET_free (key); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_free (key); + } + *(const char **) spec->ptr = pos_key; return GNUNET_OK; } +struct GNUNET_JSON_Specification +TALER_JSON_spec_otp_key (const char *name, + const char **otp_key) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_otp_key, + .field = name, + .ptr = otp_key + }; + + *otp_key = NULL; + return ret; +} + + +/** + * Parse given JSON object to `enum TALER_MerchantConfirmationAlgorithm` + * + * @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_otp_type (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + static const struct Entry + { + const char *name; + enum TALER_MerchantConfirmationAlgorithm val; + } lt [] = { + { .name = "NONE", + .val = TALER_MCA_NONE }, + { .name = "TOTP_WITHOUT_PRICE", + .val = TALER_MCA_WITHOUT_PRICE }, + { .name = "TOTP_WITH_PRICE", + .val = TALER_MCA_WITH_PRICE }, + { .name = NULL, + .val = TALER_MCA_NONE }, + }; + enum TALER_MerchantConfirmationAlgorithm *res + = (enum TALER_MerchantConfirmationAlgorithm *) spec->ptr; + + (void) cls; + if (json_is_string (root)) + { + const char *str; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (0 == strcasecmp (str, + lt[i].name)) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + } + if (json_is_integer (root)) + { + json_int_t val; + + val = json_integer_value (root); + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (val == lt[i].val) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_break_op (0); + return GNUNET_SYSERR; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_otp_type (const char *name, + enum TALER_MerchantConfirmationAlgorithm *mca) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_otp_type, + .field = name, + .ptr = mca + }; + + *mca = TALER_MCA_NONE; + return ret; +} + + /* end of json/json_helper.c */ diff --git a/src/json/json_pack.c b/src/json/json_pack.c index 6fea72a39..71c8db9d2 100644 --- a/src/json/json_pack.c +++ b/src/json/json_pack.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021 Taler Systems SA + Copyright (C) 2021, 2022 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 @@ -39,11 +39,60 @@ TALER_JSON_pack_time_abs_human (const char *name, struct GNUNET_JSON_PackSpec -TALER_JSON_pack_time_abs_nbo_human (const char *name, - struct GNUNET_TIME_AbsoluteNBO at) +TALER_JSON_pack_econtract ( + const char *name, + const struct TALER_EncryptedContract *econtract) { - return TALER_JSON_pack_time_abs_human (name, - GNUNET_TIME_absolute_ntoh (at)); + 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; } @@ -52,25 +101,41 @@ 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, }; - switch (pk->cipher) + if (NULL == pk) + return ps; + bsp = pk->bsign_pub_key; + switch (bsp->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: ps.object = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("cipher", - TALER_DENOMINATION_RSA), + GNUNET_JSON_pack_string ("cipher", + "RSA"), GNUNET_JSON_pack_uint64 ("age_mask", - pk->age_mask.mask), + pk->age_mask.bits), GNUNET_JSON_pack_rsa_public_key ("rsa_public_key", - pk->details.rsa_public_key)); - break; - default: - GNUNET_assert (0); + 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; } @@ -80,23 +145,78 @@ 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, }; - switch (sig->cipher) + if (NULL == sig) + return ps; + bs = sig->unblinded_sig; + switch (bs->cipher) { - case TALER_DENOMINATION_RSA: - ps.object - = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("cipher", - TALER_DENOMINATION_RSA), - GNUNET_JSON_pack_rsa_signature ("rsa_signature", - sig->details.rsa_signature)); + case GNUNET_CRYPTO_BSA_INVALID: break; - default: - GNUNET_assert (0); + 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; } @@ -106,50 +226,94 @@ 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, }; - switch (sig->cipher) + if (NULL == sig) + return ps; + bs = sig->blinded_sig; + switch (bs->cipher) { - case TALER_DENOMINATION_RSA: - ps.object - = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("cipher", - TALER_DENOMINATION_RSA), - GNUNET_JSON_pack_rsa_signature ("blinded_rsa_signature", - sig->details.blinded_rsa_signature)); + case GNUNET_CRYPTO_BSA_INVALID: break; - default: - GNUNET_assert (0); + 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_amount (const char *name, - const struct TALER_Amount *amount) +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, - .object = (NULL != amount) - ? TALER_JSON_from_amount (amount) - : NULL }; + 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_nbo (const char *name, - const struct TALER_AmountNBO *amount) +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_nbo (amount) + ? TALER_JSON_from_amount (amount) : NULL }; diff --git a/src/json/json_wire.c b/src/json/json_wire.c index 8f7fd6bb6..9d22d28ea 100644 --- a/src/json/json_wire.c +++ b/src/json/json_wire.c @@ -26,10 +26,10 @@ enum GNUNET_GenericReturnValue TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s, - struct TALER_MerchantWireHash *hc) + struct TALER_MerchantWireHashP *hc) { const char *payto_uri; - struct TALER_WireSalt salt; + struct TALER_WireSaltP salt; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("payto_uri", &payto_uri), @@ -70,80 +70,6 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s, } -enum GNUNET_GenericReturnValue -TALER_JSON_exchange_wire_signature_check ( - const json_t *wire_s, - const struct TALER_MasterPublicKeyP *master_pub) -{ - const char *payto_uri; - struct TALER_MasterSignatureP master_sig; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), - GNUNET_JSON_spec_fixed_auto ("master_sig", - &master_sig), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (wire_s, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - { - char *err; - - err = TALER_payto_validate (payto_uri); - if (NULL != err) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "URI `%s' ill-formed: %s\n", - payto_uri, - err); - GNUNET_free (err); - return GNUNET_SYSERR; - } - } - - return TALER_exchange_wire_signature_check (payto_uri, - master_pub, - &master_sig); -} - - -json_t * -TALER_JSON_exchange_wire_signature_make ( - const char *payto_uri, - const struct TALER_MasterPrivateKeyP *master_priv) -{ - struct TALER_MasterSignatureP master_sig; - char *err; - - if (NULL != - (err = TALER_payto_validate (payto_uri))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid payto URI `%s': %s\n", - payto_uri, - err); - GNUNET_free (err); - return NULL; - } - TALER_exchange_wire_signature_make (payto_uri, - master_priv, - &master_sig); - return GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("payto_uri", - payto_uri), - GNUNET_JSON_pack_data_auto ("master_sig", - &master_sig)); -} - - char * TALER_JSON_wire_to_payto (const json_t *wire_s) { diff --git a/src/json/test_json.c b/src/json/test_json.c index a8c1c6d8e..fba72f84b 100644 --- a/src/json/test_json.c +++ b/src/json/test_json.c @@ -91,8 +91,8 @@ path_cb (void *cls, static int test_contract (void) { - struct TALER_PrivateContractHash h1; - struct TALER_PrivateContractHash h2; + struct TALER_PrivateContractHashP h1; + struct TALER_PrivateContractHashP h2; json_t *c1; json_t *c2; json_t *c3; @@ -103,7 +103,8 @@ test_contract (void) "k2", "n1", "n2", /***/ "$forgettable", "n1", true); GNUNET_assert (GNUNET_OK == - TALER_JSON_contract_seed_forgettable (c1)); + TALER_JSON_contract_seed_forgettable (c1, + c1)); GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h1)); @@ -160,7 +161,7 @@ test_contract (void) GNUNET_assert (GNUNET_OK == TALER_JSON_contract_part_forget (c1, "k2")); - json_dumpf (c1, stderr, JSON_INDENT (2)); + // json_dumpf (c1, stderr, JSON_INDENT (2)); GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h2)); @@ -182,7 +183,7 @@ test_contract (void) GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h1)); - json_dumpf (c1, stderr, JSON_INDENT (2)); + // json_dumpf (c1, stderr, JSON_INDENT (2)); json_decref (c1); { char *s; @@ -331,9 +332,60 @@ test_contract (void) static int +test_json_canon (void) +{ + { + json_t *c1; + char *canon; + c1 = json_pack ("{s:s}", + "k1", "Hello\nWorld"); + + canon = TALER_JSON_canonicalize (c1); + GNUNET_assert (NULL != canon); + + printf ("canon: '%s'\n", canon); + + GNUNET_assert (0 == strcmp (canon, + "{\"k1\":\"Hello\\nWorld\"}")); + } + { + json_t *c1; + char *canon; + c1 = json_pack ("{s:s}", + "k1", "Testing “unicode” characters"); + + canon = TALER_JSON_canonicalize (c1); + GNUNET_assert (NULL != canon); + + printf ("canon: '%s'\n", canon); + + GNUNET_assert (0 == strcmp (canon, + "{\"k1\":\"Testing “unicode” characters\"}")); + } + { + json_t *c1; + char *canon; + c1 = json_pack ("{s:s}", + "k1", "low range \x05 chars"); + + canon = TALER_JSON_canonicalize (c1); + GNUNET_assert (NULL != canon); + + printf ("canon: '%s'\n", canon); + + GNUNET_assert (0 == strcmp (canon, + "{\"k1\":\"low range \\u0005 chars\"}")); + } + + + return 0; +} + + +static int test_rfc8785 (void) { - struct TALER_PrivateContractHash h1; + struct TALER_PrivateContractHashP h1; json_t *c1; c1 = json_pack ("{s:s}", @@ -348,7 +400,7 @@ test_rfc8785 (void) sizeof (h1)); if (0 != strcmp (s, - "J678K3PW9Y3DG63Z3T7ZYR2P7CEXMVZ2SFPQMABACK9TJRYREPP82542PCJ0P7Y7FAQAMWECDX50XH1RBTWHX6SSJHH6FXRV0JCS6R8")) + "531S33T8ZRGW6548G7T67PMDNGS4Z1D8A2GMB87G3PNKYTW6KGF7Q99XVCGXBKVA2HX6PR5ENJ1PQ5ZTYMMXQB6RM7S82VP7ZG2X5G8")) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid reference hash: %s\n", @@ -377,6 +429,8 @@ main (int argc, return 1; if (0 != test_contract ()) return 2; + if (0 != test_json_canon ()) + return 2; if (0 != test_rfc8785 ()) return 2; return 0; diff --git a/src/json/test_json_wire.c b/src/json/test_json_wire.c deleted file mode 100644 index b417b25fe..000000000 --- a/src/json/test_json_wire.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of TALER - (C) 2015, 2016 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/test_json_wire.c - * @brief Tests for Taler-specific crypto logic - * @author Christian Grothoff <christian@grothoff.org> - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_json_lib.h" - - -int -main (int argc, - const char *const argv[]) -{ - struct TALER_MasterPublicKeyP master_pub; - struct TALER_MasterPrivateKeyP master_priv; - json_t *wire_xtalerbank; - json_t *wire_iban; - const char *payto_xtalerbank = "payto://x-taler-bank/42"; - const char *payto_iban = - "payto://iban/BIC-TO-BE-SKIPPED/DE89370400440532013000?receiver-name=Test"; - char *p_xtalerbank; - char *p_iban; - - (void) argc; - (void) argv; - GNUNET_log_setup ("test-json-wire", - "WARNING", - NULL); - GNUNET_CRYPTO_eddsa_key_create (&master_priv.eddsa_priv); - GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, - &master_pub.eddsa_pub); - wire_xtalerbank = TALER_JSON_exchange_wire_signature_make (payto_xtalerbank, - &master_priv); - wire_iban = TALER_JSON_exchange_wire_signature_make (payto_iban, - &master_priv); - if (NULL == wire_iban) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not parse payto/IBAN (%s) into 'wire object'\n", - payto_iban); - return 1; - } - p_xtalerbank = TALER_JSON_wire_to_payto (wire_xtalerbank); - p_iban = TALER_JSON_wire_to_payto (wire_iban); - GNUNET_assert (0 == strcmp (p_xtalerbank, payto_xtalerbank)); - GNUNET_assert (0 == strcmp (p_iban, payto_iban)); - GNUNET_free (p_xtalerbank); - GNUNET_free (p_iban); - - GNUNET_assert (GNUNET_OK == - TALER_JSON_exchange_wire_signature_check (wire_xtalerbank, - &master_pub)); - GNUNET_assert (GNUNET_OK == - TALER_JSON_exchange_wire_signature_check (wire_iban, - &master_pub)); - json_decref (wire_xtalerbank); - json_decref (wire_iban); - - return 0; -} - - -/* end of test_json_wire.c */ |