/* This file is part of TALER Copyright (C) 2015-2021 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 */ /** * @file lib/exchange_api_refresh_common.c * @brief Serialization logic shared between melt and reveal steps during refreshing * @author Christian Grothoff */ #include "platform.h" #include "exchange_api_refresh_common.h" /** * Free all information associated with a melted coin session. * * @param mc melted coin to release, the pointer itself is NOT * freed (as it is typically not allocated by itself) */ static void free_melted_coin (struct MeltedCoin *mc) { TALER_denom_pub_free (&mc->pub_key); TALER_denom_sig_free (&mc->sig); } void TALER_EXCHANGE_free_melt_data_ (struct MeltData *md) { free_melted_coin (&md->melted_coin); if (NULL != md->fresh_pks) { for (unsigned int i = 0; inum_fresh_coins; i++) TALER_denom_pub_free (&md->fresh_pks[i]); GNUNET_free (md->fresh_pks); } for (unsigned int i = 0; ifresh_coins[i]); /* Finally, clean up a bit... */ GNUNET_CRYPTO_zero_keys (md, sizeof (struct MeltData)); } /** * Serialize information about a coin we are melting. * * @param mc information to serialize * @return NULL on error */ static json_t * serialize_melted_coin (const struct MeltedCoin *mc) { json_t *tprivs; tprivs = json_array (); GNUNET_assert (NULL != tprivs); for (unsigned int i = 0; itransfer_priv[i])))); return GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("coin_priv", &mc->coin_priv), TALER_JSON_pack_denom_sig ("denom_sig", &mc->sig), TALER_JSON_pack_denom_pub ("denom_pub", &mc->pub_key), TALER_JSON_pack_amount ("melt_amount_with_fee", &mc->melt_amount_with_fee), TALER_JSON_pack_amount ("original_value", &mc->original_value), TALER_JSON_pack_amount ("melt_fee", &mc->fee_melt), GNUNET_JSON_pack_time_abs ("expire_deposit", mc->expire_deposit), GNUNET_JSON_pack_array_steal ("transfer_privs", tprivs)); } /** * Deserialize information about a coin we are melting. * * @param[out] mc information to deserialize * @param currency expected currency * @param in JSON object to read data from * @return #GNUNET_NO to report errors */ static enum GNUNET_GenericReturnValue deserialize_melted_coin (struct MeltedCoin *mc, const char *currency, const json_t *in) { json_t *trans_privs; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_priv", &mc->coin_priv), TALER_JSON_spec_denom_sig ("denom_sig", &mc->sig), TALER_JSON_spec_denom_pub ("denom_pub", &mc->pub_key), TALER_JSON_spec_amount ("melt_amount_with_fee", currency, &mc->melt_amount_with_fee), TALER_JSON_spec_amount ("original_value", currency, &mc->original_value), TALER_JSON_spec_amount ("melt_fee", currency, &mc->fee_melt), TALER_JSON_spec_absolute_time ("expire_deposit", &mc->expire_deposit), GNUNET_JSON_spec_json ("transfer_privs", &trans_privs), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (in, spec, NULL, NULL)) { GNUNET_break_op (0); return GNUNET_NO; } if (TALER_CNC_KAPPA != json_array_size (trans_privs)) { GNUNET_JSON_parse_free (spec); GNUNET_break_op (0); return GNUNET_NO; } for (unsigned int i = 0; itransfer_priv[i]), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (json_array_get (trans_privs, i), spec, NULL, NULL)) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); return GNUNET_NO; } } json_decref (trans_privs); return GNUNET_OK; } /** * Serialize melt data. * * @param md data to serialize * @return serialized melt data */ static json_t * serialize_melt_data (const struct MeltData *md) { json_t *fresh_coins; fresh_coins = json_array (); GNUNET_assert (NULL != fresh_coins); for (int i = 0; inum_fresh_coins; i++) { json_t *planchet_secrets; planchet_secrets = json_array (); GNUNET_assert (NULL != planchet_secrets); for (unsigned int j = 0; jfresh_coins[j][i])); GNUNET_assert (0 == json_array_append_new (planchet_secrets, ps)); } GNUNET_assert (0 == json_array_append_new ( fresh_coins, GNUNET_JSON_PACK ( TALER_JSON_pack_denom_pub ("denom_pub", &md->fresh_pks[i]), GNUNET_JSON_pack_array_steal ("planchet_secrets", planchet_secrets))) ); } return GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_steal ("fresh_coins", fresh_coins), GNUNET_JSON_pack_object_steal ("melted_coin", serialize_melted_coin (&md->melted_coin)), GNUNET_JSON_pack_data_auto ("rc", &md->rc)); } struct MeltData * TALER_EXCHANGE_deserialize_melt_data_ (const json_t *melt_data, const char *currency) { struct MeltData *md = GNUNET_new (struct MeltData); json_t *fresh_coins; json_t *melted_coin; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("rc", &md->rc), GNUNET_JSON_spec_json ("melted_coin", &melted_coin), GNUNET_JSON_spec_json ("fresh_coins", &fresh_coins), GNUNET_JSON_spec_end () }; bool ok; if (GNUNET_OK != GNUNET_JSON_parse (melt_data, spec, NULL, NULL)) { GNUNET_break (0); GNUNET_JSON_parse_free (spec); GNUNET_free (md); return NULL; } if (! (json_is_array (fresh_coins) && json_is_object (melted_coin)) ) { GNUNET_break (0); GNUNET_JSON_parse_free (spec); return NULL; } if (GNUNET_OK != deserialize_melted_coin (&md->melted_coin, currency, melted_coin)) { GNUNET_break (0); GNUNET_JSON_parse_free (spec); return NULL; } md->num_fresh_coins = json_array_size (fresh_coins); md->fresh_pks = GNUNET_new_array (md->num_fresh_coins, struct TALER_DenominationPublicKey); for (unsigned int i = 0; ifresh_coins[i] = GNUNET_new_array (md->num_fresh_coins, struct TALER_PlanchetSecretsP); ok = true; for (unsigned int i = 0; inum_fresh_coins; i++) { const json_t *ji = json_array_get (fresh_coins, i); json_t *planchet_secrets; struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_json ("planchet_secrets", &planchet_secrets), TALER_JSON_spec_denom_pub ("denom_pub", &md->fresh_pks[i]), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (ji, ispec, NULL, NULL)) { GNUNET_break (0); ok = false; break; } if ( (! json_is_array (planchet_secrets)) || (TALER_CNC_KAPPA != json_array_size (planchet_secrets)) ) { GNUNET_break (0); ok = false; GNUNET_JSON_parse_free (ispec); break; } for (unsigned int j = 0; jfresh_coins[j][i]), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (json_array_get (planchet_secrets, j), jspec, NULL, NULL)) { GNUNET_break (0); ok = false; break; } } json_decref (planchet_secrets); if (! ok) break; } GNUNET_JSON_parse_free (spec); if (! ok) { TALER_EXCHANGE_free_melt_data_ (md); GNUNET_free (md); return NULL; } return md; } json_t * TALER_EXCHANGE_refresh_prepare ( const struct TALER_CoinSpendPrivateKeyP *melt_priv, const struct TALER_Amount *melt_amount, const struct TALER_DenominationSignature *melt_sig, const struct TALER_EXCHANGE_DenomPublicKey *melt_pk, unsigned int fresh_pks_len, const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks) { struct MeltData md; json_t *ret; struct TALER_Amount total; struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA]; struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; GNUNET_CRYPTO_eddsa_key_get_public (&melt_priv->eddsa_priv, &coin_pub.eddsa_pub); /* build up melt data structure */ memset (&md, 0, sizeof (md)); md.num_fresh_coins = fresh_pks_len; md.melted_coin.coin_priv = *melt_priv; md.melted_coin.melt_amount_with_fee = *melt_amount; md.melted_coin.fee_melt = melt_pk->fee_refresh; md.melted_coin.original_value = melt_pk->value; md.melted_coin.expire_deposit = melt_pk->expire_deposit; GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (melt_amount->currency, &total)); TALER_denom_pub_deep_copy (&md.melted_coin.pub_key, &melt_pk->key); TALER_denom_sig_deep_copy (&md.melted_coin.sig, melt_sig); md.fresh_pks = GNUNET_new_array (fresh_pks_len, struct TALER_DenominationPublicKey); for (unsigned int i = 0; i TALER_amount_add (&total, &total, &fresh_pks[i].value)) || (0 > TALER_amount_add (&total, &total, &fresh_pks[i].fee_withdraw)) ) { GNUNET_break (0); TALER_EXCHANGE_free_melt_data_ (&md); return NULL; } } /* verify that melt_amount is above total cost */ if (1 == TALER_amount_cmp (&total, melt_amount) ) { /* Eh, this operation is more expensive than the @a melt_amount. This is not OK. */ GNUNET_break (0); TALER_EXCHANGE_free_melt_data_ (&md); return NULL; } /* build up coins */ for (unsigned int i = 0; idk = &md.fresh_pks[j]; rcd->coin_ev = pd.coin_ev; rcd->coin_ev_size = pd.coin_ev_size; } } /* Compute refresh commitment */ TALER_refresh_get_commitment (&md.rc, TALER_CNC_KAPPA, fresh_pks_len, rce, &coin_pub, melt_amount); /* finally, serialize everything */ ret = serialize_melt_data (&md); for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) { for (unsigned int j = 0; j < fresh_pks_len; j++) GNUNET_free (rce[i].new_coins[j].coin_ev); GNUNET_free (rce[i].new_coins); } TALER_EXCHANGE_free_melt_data_ (&md); return ret; }