diff options
Diffstat (limited to 'src/lib/exchange_api_refreshes_reveal.c')
-rw-r--r-- | src/lib/exchange_api_refreshes_reveal.c | 320 |
1 files changed, 180 insertions, 140 deletions
diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c index 2b7fcf8cf..69c53a6c9 100644 --- a/src/lib/exchange_api_refreshes_reveal.c +++ b/src/lib/exchange_api_refreshes_reveal.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA + Copyright (C) 2015-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 @@ -40,11 +40,6 @@ struct TALER_EXCHANGE_RefreshesRevealHandle { /** - * The connection to exchange this request handle will use - */ - struct TALER_EXCHANGE_Handle *exchange; - - /** * The url for this request. */ char *url; @@ -61,6 +56,11 @@ struct TALER_EXCHANGE_RefreshesRevealHandle struct GNUNET_CURL_Job *job; /** + * Exchange-contributed values to the operation. + */ + struct TALER_ExchangeWithdrawValues *alg_values; + + /** * Function to call with the result. */ TALER_EXCHANGE_RefreshesRevealCallback reveal_cb; @@ -73,7 +73,7 @@ struct TALER_EXCHANGE_RefreshesRevealHandle /** * Actual information about the melt operation. */ - struct MeltData *md; + struct MeltData md; /** * The index selected by the exchange in cut-and-choose to not be revealed. @@ -84,29 +84,28 @@ struct TALER_EXCHANGE_RefreshesRevealHandle /** - * We got a 200 OK response for the /refreshes/$RCH/reveal operation. - * Extract the coin signatures and return them to the caller. - * The signatures we get from the exchange is for the blinded value. - * Thus, we first must unblind them and then should verify their - * validity. + * We got a 200 OK response for the /refreshes/$RCH/reveal operation. Extract + * the coin signatures and return them to the caller. The signatures we get + * from the exchange is for the blinded value. Thus, we first must unblind + * them and then should verify their validity. * * If everything checks out, we return the unblinded signatures * to the application via the callback. * * @param rrh operation handle * @param json reply from the exchange - * @param[out] sigs array of length `num_fresh_coins`, initialized to contain RSA signatures + * @param[out] rcis array of length `num_fresh_coins`, initialized to contain the coin data * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors */ static enum GNUNET_GenericReturnValue refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, const json_t *json, - struct TALER_DenominationSignature *sigs) + struct TALER_EXCHANGE_RevealedCoinInfo *rcis) { - json_t *jsona; + const json_t *jsona; struct GNUNET_JSON_Specification outer_spec[] = { - GNUNET_JSON_spec_json ("ev_sigs", - &jsona), + GNUNET_JSON_spec_array_const ("ev_sigs", + &jsona), GNUNET_JSON_spec_end () }; @@ -118,39 +117,44 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (! json_is_array (jsona)) - { - /* We expected an array of coins */ - GNUNET_break_op (0); - GNUNET_JSON_parse_free (outer_spec); - return GNUNET_SYSERR; - } - if (rrh->md->num_fresh_coins != json_array_size (jsona)) + if (rrh->md.num_fresh_coins != json_array_size (jsona)) { /* Number of coins generated does not match our expectation */ GNUNET_break_op (0); - GNUNET_JSON_parse_free (outer_spec); return GNUNET_SYSERR; } - for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++) + for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) { - const struct TALER_PlanchetSecretsP *fc; - struct TALER_DenominationPublicKey *pk; + struct TALER_EXCHANGE_RevealedCoinInfo *rci = &rcis[i]; + const struct FreshCoinData *fcd = &rrh->md.fcds[i]; + const struct TALER_DenominationPublicKey *pk; json_t *jsonai; struct TALER_BlindedDenominationSignature blind_sig; struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_CoinPubHash coin_hash; + struct TALER_CoinPubHashP coin_hash; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_blinded_denom_sig ("ev_sig", &blind_sig), GNUNET_JSON_spec_end () }; struct TALER_FreshCoin coin; + union GNUNET_CRYPTO_BlindingSecretP bks; + const struct TALER_AgeCommitmentHash *pah = NULL; - fc = &rrh->md->fresh_coins[rrh->noreveal_index][i]; - pk = &rrh->md->fresh_pks[i]; + rci->ps = fcd->ps[rrh->noreveal_index]; + rci->bks = fcd->bks[rrh->noreveal_index]; + rci->age_commitment_proof = NULL; + pk = &fcd->fresh_pk; jsonai = json_array_get (jsona, i); GNUNET_assert (NULL != jsonai); + if (NULL != rrh->md.melted_coin.age_commitment_proof) + { + rci->age_commitment_proof + = fcd->age_commitment_proofs[rrh->noreveal_index]; + TALER_age_commitment_hash (&rci->age_commitment_proof->commitment, + &rci->h_age_commitment); + pah = &rci->h_age_commitment; + } if (GNUNET_OK != GNUNET_JSON_parse (jsonai, @@ -158,34 +162,41 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, NULL, NULL)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (outer_spec); return GNUNET_SYSERR; } + TALER_planchet_setup_coin_priv (&rci->ps, + &rrh->alg_values[i], + &rci->coin_priv); + TALER_planchet_blinding_secret_create (&rci->ps, + &rrh->alg_values[i], + &bks); /* needed to verify the signature, and we didn't store it earlier, hence recomputing it here... */ - GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, + GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv, &coin_pub.eddsa_pub); - /* FIXME-Oec: Age commitment hash. */ - TALER_coin_pub_hash (&coin_pub, - NULL, /* FIXME-Oec */ - &coin_hash); + TALER_coin_pub_hash ( + &coin_pub, + pah, + &coin_hash); if (GNUNET_OK != - TALER_planchet_to_coin (pk, - &blind_sig, - fc, - &coin_hash, - &coin)) + TALER_planchet_to_coin ( + pk, + &blind_sig, + &bks, + &rci->coin_priv, + pah, + &coin_hash, + &rrh->alg_values[i], + &coin)) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); - GNUNET_JSON_parse_free (outer_spec); return GNUNET_SYSERR; } GNUNET_JSON_parse_free (spec); - sigs[i] = coin.sig; + rci->sig = coin.sig; } - GNUNET_JSON_parse_free (outer_spec); return GNUNET_OK; } @@ -205,96 +216,101 @@ handle_refresh_reveal_finished (void *cls, { struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls; const json_t *j = response; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = (unsigned int) response_code + struct TALER_EXCHANGE_RevealResult rr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code }; rrh->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: { - struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins]; - int ret; + struct TALER_EXCHANGE_RevealedCoinInfo rcis[rrh->md.num_fresh_coins]; + enum GNUNET_GenericReturnValue ret; - memset (sigs, + memset (rcis, 0, - sizeof (sigs)); + sizeof (rcis)); ret = refresh_reveal_ok (rrh, j, - sigs); + rcis); if (GNUNET_OK != ret) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rr.hr.http_status = 0; + rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; } else { + GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA); + rr.details.ok.num_coins = rrh->md.num_fresh_coins; + rr.details.ok.coins = rcis; rrh->reveal_cb (rrh->reveal_cb_cls, - &hr, - rrh->md->num_fresh_coins, - rrh->md->fresh_coins[rrh->noreveal_index], - sigs); + &rr); rrh->reveal_cb = NULL; } - for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++) - TALER_denom_sig_free (&sigs[i]); + for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) + { + TALER_denom_sig_free (&rcis[i].sig); + TALER_age_commitment_proof_free (rcis[i].age_commitment_proof); + } TALER_EXCHANGE_refreshes_reveal_cancel (rrh); return; } case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_CONFLICT: /* Nothing really to verify, exchange says our reveal is inconsistent with our commitment, so either side is buggy; we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_GONE: /* Server claims key expired or has been revoked */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); break; default: /* unexpected response code */ GNUNET_break_op (0); - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d for exchange refreshes reveal\n", (unsigned int) response_code, - (int) hr.ec); + (int) rr.hr.ec); break; } if (NULL != rrh->reveal_cb) rrh->reveal_cb (rrh->reveal_cb_cls, - &hr, - 0, - NULL, - NULL); + &rr); TALER_EXCHANGE_refreshes_reveal_cancel (rrh); } struct TALER_EXCHANGE_RefreshesRevealHandle * TALER_EXCHANGE_refreshes_reveal ( - struct TALER_EXCHANGE_Handle *exchange, - const json_t *refresh_data, + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_EXCHANGE_RefreshData *rd, + unsigned int num_coins, + const struct TALER_ExchangeWithdrawValues alg_values[static num_coins], uint32_t noreveal_index, TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, void *reveal_cb_cls) @@ -305,12 +321,13 @@ TALER_EXCHANGE_refreshes_reveal ( json_t *coin_evs; json_t *reveal_obj; json_t *link_sigs; + json_t *old_age_commitment = NULL; CURL *eh; - struct GNUNET_CURL_Context *ctx; - struct MeltData *md; - struct TALER_TransferPublicKeyP transfer_pub; + struct MeltData md; char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32]; + bool send_rms = false; + GNUNET_assert (num_coins == rd->fresh_pks_len); if (noreveal_index >= TALER_CNC_KAPPA) { /* We check this here, as it would be really bad to below just @@ -320,73 +337,59 @@ TALER_EXCHANGE_refreshes_reveal ( GNUNET_break (0); return NULL; } - if (GNUNET_YES != - TEAH_handle_is_ready (exchange)) - { - GNUNET_break (0); - return NULL; - } - md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data, - exchange->key_data.currency); - if (NULL == md) + if (GNUNET_OK != + TALER_EXCHANGE_get_melt_data_ (rms, + rd, + alg_values, + &md)) { GNUNET_break (0); return NULL; } - /* now transfer_pub */ - GNUNET_CRYPTO_ecdhe_key_get_public ( - &md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv, - &transfer_pub.ecdhe_pub); - /* now new_denoms */ GNUNET_assert (NULL != (new_denoms_h = json_array ())); GNUNET_assert (NULL != (coin_evs = json_array ())); GNUNET_assert (NULL != (link_sigs = json_array ())); - for (unsigned int i = 0; i<md->num_fresh_coins; i++) + for (unsigned int i = 0; i<md.num_fresh_coins; i++) { - struct TALER_DenominationHash denom_hash; - struct TALER_PlanchetDetail pd; - struct TALER_CoinPubHash c_hash; + const struct TALER_RefreshCoinData *rcd = &md.rcd[noreveal_index][i]; + struct TALER_DenominationHashP denom_hash; - TALER_denom_pub_hash (&md->fresh_pks[i], + if (GNUNET_CRYPTO_BSA_CS == + md.fcds[i].fresh_pk.bsign_pub_key->cipher) + send_rms = true; + TALER_denom_pub_hash (&md.fcds[i].fresh_pk, &denom_hash); GNUNET_assert (0 == json_array_append_new (new_denoms_h, GNUNET_JSON_from_data_auto ( &denom_hash))); - - if (GNUNET_OK != - TALER_planchet_prepare (&md->fresh_pks[i], - &md->fresh_coins[noreveal_index][i], - &c_hash, - &pd)) - { - /* This should have been noticed during the preparation stage. */ - GNUNET_break (0); - json_decref (new_denoms_h); - json_decref (coin_evs); - return NULL; - } GNUNET_assert (0 == - json_array_append_new (coin_evs, - GNUNET_JSON_from_data (pd.coin_ev, - pd.coin_ev_size))); + json_array_append_new ( + coin_evs, + GNUNET_JSON_PACK ( + TALER_JSON_pack_blinded_planchet ( + NULL, + &rcd->blinded_planchet)))); { struct TALER_CoinSpendSignatureP link_sig; - - TALER_wallet_link_sign (&denom_hash, - &transfer_pub, - pd.coin_ev, - pd.coin_ev_size, - &md->melted_coin.coin_priv, - &link_sig); + struct TALER_BlindedCoinHashP bch; + + TALER_coin_ev_hash (&rcd->blinded_planchet, + &denom_hash, + &bch); + TALER_wallet_link_sign ( + &denom_hash, + &md.transfer_pub[noreveal_index], + &bch, + &md.melted_coin.coin_priv, + &link_sig); GNUNET_assert (0 == json_array_append_new ( link_sigs, GNUNET_JSON_from_data_auto (&link_sig))); } - GNUNET_free (pd.coin_ev); } /* build array of transfer private keys */ @@ -395,20 +398,46 @@ TALER_EXCHANGE_refreshes_reveal ( { if (j == noreveal_index) { - /* This is crucial: exclude the transfer key for the - noreval index! */ + /* This is crucial: exclude the transfer key for the noreval index! */ continue; } GNUNET_assert (0 == json_array_append_new (transfer_privs, GNUNET_JSON_from_data_auto ( - &md->melted_coin.transfer_priv[j]))); + &md.transfer_priv[j]))); + } + + /* build array of old age commitment, if applicable */ + if (NULL != rd->melt_age_commitment_proof) + { + GNUNET_assert (NULL != rd->melt_h_age_commitment); + GNUNET_assert (NULL != (old_age_commitment = json_array ())); + + for (size_t i = 0; i < rd->melt_age_commitment_proof->commitment.num; i++) + { + enum GNUNET_GenericReturnValue ret; + + ret = json_array_append_new ( + old_age_commitment, + GNUNET_JSON_from_data_auto ( + &rd->melt_age_commitment_proof->commitment.keys[i])); + GNUNET_assert (0 == ret); + } } /* build main JSON request */ reveal_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("transfer_pub", - &transfer_pub), + &md.transfer_pub[noreveal_index]), + GNUNET_JSON_pack_allow_null ( + send_rms + ? GNUNET_JSON_pack_data_auto ("rms", + rms) + : GNUNET_JSON_pack_string ("rms", + NULL)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("old_age_commitment", + old_age_commitment)), GNUNET_JSON_pack_array_steal ("transfer_privs", transfer_privs), GNUNET_JSON_pack_array_steal ("link_sigs", @@ -421,32 +450,40 @@ TALER_EXCHANGE_refreshes_reveal ( char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2]; char *end; - end = GNUNET_STRINGS_data_to_string (&md->rc, - sizeof (struct - TALER_RefreshCommitmentP), + end = GNUNET_STRINGS_data_to_string (&md.rc, + sizeof (md.rc), pub_str, sizeof (pub_str)); *end = '\0'; GNUNET_snprintf (arg_str, sizeof (arg_str), - "/refreshes/%s/reveal", + "refreshes/%s/reveal", pub_str); } /* finally, we can actually issue the request */ rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle); - rrh->exchange = exchange; rrh->noreveal_index = noreveal_index; rrh->reveal_cb = reveal_cb; rrh->reveal_cb_cls = reveal_cb_cls; rrh->md = md; - rrh->url = TEAH_path_to_url (rrh->exchange, - arg_str); + rrh->alg_values + = GNUNET_new_array (md.num_fresh_coins, + struct TALER_ExchangeWithdrawValues); + for (unsigned int i = 0; i<md.num_fresh_coins; i++) + TALER_denom_ewv_copy (&rrh->alg_values[i], + &alg_values[i]); + rrh->url = TALER_url_join (url, + arg_str, + NULL); if (NULL == rrh->url) { json_decref (reveal_obj); + TALER_EXCHANGE_free_melt_data_ (&md); + GNUNET_free (rrh->alg_values); GNUNET_free (rrh); return NULL; } + eh = TALER_EXCHANGE_curl_easy_get_ (rrh->url); if ( (NULL == eh) || (GNUNET_OK != @@ -458,12 +495,13 @@ TALER_EXCHANGE_refreshes_reveal ( if (NULL != eh) curl_easy_cleanup (eh); json_decref (reveal_obj); + TALER_EXCHANGE_free_melt_data_ (&md); + GNUNET_free (rrh->alg_values); GNUNET_free (rrh->url); GNUNET_free (rrh); return NULL; } json_decref (reveal_obj); - ctx = TEAH_handle_to_context (rrh->exchange); rrh->job = GNUNET_CURL_job_add2 (ctx, eh, rrh->ctx.headers, @@ -482,10 +520,12 @@ TALER_EXCHANGE_refreshes_reveal_cancel ( GNUNET_CURL_job_cancel (rrh->job); rrh->job = NULL; } + for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) + TALER_denom_ewv_free (&rrh->alg_values[i]); + GNUNET_free (rrh->alg_values); GNUNET_free (rrh->url); TALER_curl_easy_post_finished (&rrh->ctx); - TALER_EXCHANGE_free_melt_data_ (rrh->md); /* does not free 'md' itself */ - GNUNET_free (rrh->md); + TALER_EXCHANGE_free_melt_data_ (&rrh->md); GNUNET_free (rrh); } |