From 2f2930f1ba0f1708fc4455c66173fd61188a3369 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 31 Oct 2017 14:02:54 +0100 Subject: major API refactoring, adding planchet generation and coin extraction APIs to the Taler crypto library, thereby simplifying code in withdraw, refresh, tipping, payback and testcases; slight API incompatibilities to previous versions are introduced --- ChangeLog | 6 ++ src/benchmark/taler-exchange-benchmark.c | 6 +- src/exchange-lib/exchange_api_payback.c | 14 ++- src/exchange-lib/exchange_api_refresh.c | 119 ++++++++------------ src/exchange-lib/exchange_api_reserve.c | 82 ++++++-------- src/exchange-lib/test_exchange_api.c | 46 +++----- src/include/taler_crypto_lib.h | 180 ++++++++++++++++++++++++------- src/include/taler_exchange_service.h | 13 +-- src/include/taler_util.h | 2 - src/util/crypto.c | 137 +++++++++++++++++++---- src/util/test_crypto.c | 51 ++++++++- 11 files changed, 416 insertions(+), 240 deletions(-) diff --git a/ChangeLog b/ChangeLog index ab96d8aca..2f123098b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Tue Oct 31 13:43:55 CET 2017 + Extended crypto API to provide planchet generation functions + explicitly (to be used on withdraw, refresh and tipping). + Renamed a few API calls to better reflect their functions. + Changed a few function signatures to use new structs. -CG + Wed Oct 18 15:20:29 CEST 2017 Releasing Taler v0.4.0. -CG diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index 93afb683b..a90773ba0 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -981,6 +981,7 @@ withdraw_coin (struct Coin *coin) struct TALER_Amount left; const struct TALER_EXCHANGE_Keys *keys; struct Reserve *r; + struct TALER_PlanchetSecretsP ps; keys = TALER_EXCHANGE_get_keys (exchange); r = &reserves[coin->reserve_index]; @@ -996,12 +997,13 @@ withdraw_coin (struct Coin *coin) GNUNET_assert (NULL != (coin->pk = find_pk (keys, &amount))); if (warm >= WARM_THRESHOLD) num_withdraw++; + ps.coin_priv = coin->coin_priv; + ps.blinding_key = blinding_key; coin->wsh = TALER_EXCHANGE_reserve_withdraw (exchange, coin->pk, &r->reserve_priv, - &coin->coin_priv, - &blinding_key, + &ps, &reserve_withdraw_cb, coin); GNUNET_assert (GNUNET_SYSERR != diff --git a/src/exchange-lib/exchange_api_payback.c b/src/exchange-lib/exchange_api_payback.c index 40b713857..ec0cda150 100644 --- a/src/exchange-lib/exchange_api_payback.c +++ b/src/exchange-lib/exchange_api_payback.c @@ -260,8 +260,7 @@ handle_payback_finished (void *cls, * @param exchange the exchange handle; the exchange must be ready to operate * @param pk kind of coin to pay back * @param denom_sig signature over the coin by the exchange using @a pk - * @param coin_priv the coin's private key, - * @param blinding_key where to fetch the coin's blinding key + * @param ps secret internals of the original planchet * @param payback_cb the callback to call when the final result for this request is available * @param payback_cb_cls closure for @a payback_cb * @return NULL @@ -272,8 +271,7 @@ struct TALER_EXCHANGE_PaybackHandle * TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange, const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKeyP *blinding_key, + const struct TALER_PlanchetSecretsP *ps, TALER_EXCHANGE_PaybackResultCallback payback_cb, void *payback_cb_cls) { @@ -288,12 +286,12 @@ TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange, MAH_handle_is_ready (exchange)); pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK); pr.purpose.size = htonl (sizeof (struct TALER_PaybackRequestPS)); - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + GNUNET_CRYPTO_eddsa_key_get_public (&ps->coin_priv.eddsa_priv, &pr.coin_pub.eddsa_pub); pr.h_denom_pub = pk->h_key; - pr.coin_blind = *blinding_key; + pr.coin_blind = ps->blinding_key; GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv, + GNUNET_CRYPTO_eddsa_sign (&ps->coin_priv.eddsa_priv, &pr.purpose, &coin_sig.eddsa_signature)); @@ -304,7 +302,7 @@ TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange, "denom_sig", GNUNET_JSON_from_rsa_signature (denom_sig->rsa_signature), "coin_pub", GNUNET_JSON_from_data_auto (&pr.coin_pub), "coin_sig", GNUNET_JSON_from_data_auto (&coin_sig), - "coin_blind_key_secret", GNUNET_JSON_from_data_auto (blinding_key) + "coin_blind_key_secret", GNUNET_JSON_from_data_auto (&ps->blinding_key) ); if (NULL == payback_obj) { diff --git a/src/exchange-lib/exchange_api_refresh.c b/src/exchange-lib/exchange_api_refresh.c index 510b6e409..305747f63 100644 --- a/src/exchange-lib/exchange_api_refresh.c +++ b/src/exchange-lib/exchange_api_refresh.c @@ -236,18 +236,16 @@ free_melted_coin (struct MeltedCoin *mc) static void free_melt_data (struct MeltData *md) { - unsigned int i; - free_melted_coin (&md->melted_coin); if (NULL != md->fresh_pks) { - for (i=0;inum_fresh_coins;i++) + for (unsigned int i=0;inum_fresh_coins;i++) if (NULL != md->fresh_pks[i].rsa_public_key) GNUNET_CRYPTO_rsa_public_key_free (md->fresh_pks[i].rsa_public_key); GNUNET_free (md->fresh_pks); } - for (i=0;ifresh_coins[i]); /* Finally, clean up a bit... (NOTE: compilers might optimize this away, so this is @@ -600,8 +598,6 @@ deserialize_melt_data (const char *buf, { struct MeltData *md; struct MeltDataP mdp; - unsigned int i; - unsigned int j; size_t off; int ok; @@ -615,7 +611,7 @@ deserialize_melt_data (const char *buf, md->num_fresh_coins = ntohs (mdp.num_fresh_coins); md->fresh_pks = GNUNET_new_array (md->num_fresh_coins, struct TALER_DenominationPublicKey); - for (i=0;ifresh_coins[i] = GNUNET_new_array (md->num_fresh_coins, struct TALER_PlanchetSecretsP); off = sizeof (struct MeltDataP); @@ -624,14 +620,14 @@ deserialize_melt_data (const char *buf, &buf[off], buf_size - off, &ok); - for (i=0;(inum_fresh_coins)&&(GNUNET_YES == ok);i++) + for (unsigned int i=0;(inum_fresh_coins)&&(GNUNET_YES == ok);i++) off += deserialize_denomination_key (&md->fresh_pks[i], &buf[off], buf_size - off, &ok); - for (i=0;inum_fresh_coins)&&(GNUNET_YES == ok);j++) + for (unsigned int i=0;inum_fresh_coins)&&(GNUNET_YES == ok);j++) off += deserialize_fresh_coin (&md->fresh_coins[i][j], &buf[off], buf_size - off, @@ -704,8 +700,6 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr { struct MeltData md; char *buf; - unsigned int i; - unsigned int j; struct GNUNET_HashContext *hash_context; struct TALER_Amount total; struct TALER_CoinSpendPublicKeyP coin_pub; @@ -715,7 +709,7 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr &coin_pub.eddsa_pub); hash_context = GNUNET_CRYPTO_hash_context_start (); /* build up melt data structure */ - for (i=0;irsa_signature); md.fresh_pks = GNUNET_new_array (fresh_pks_len, struct TALER_DenominationPublicKey); - for (i=0;icurrency, &total)); - for (j=0;jcoin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &coin_hash); - if (GNUNET_YES != - GNUNET_CRYPTO_rsa_blind (&coin_hash, - &fc->blinding_key.bks, - md.fresh_pks[j].rsa_public_key, - &coin_ev, - &coin_ev_size)) + if (GNUNET_OK != + TALER_planchet_prepare (&md.fresh_pks[j], + fc, + &pd)) { GNUNET_break_op (0); GNUNET_CRYPTO_hash_context_abort (hash_context); @@ -849,9 +833,9 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr return NULL; } GNUNET_CRYPTO_hash_context_read (hash_context, - coin_ev, - coin_ev_size); - GNUNET_free (coin_ev); + pd.coin_ev, + pd.coin_ev_size); + GNUNET_free (pd.coin_ev); } } GNUNET_CRYPTO_hash_context_finish (hash_context, @@ -1294,22 +1278,12 @@ TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange, for (i=0;inum_fresh_coins;i++) { const struct TALER_PlanchetSecretsP *fc = &md->fresh_coins[j][i]; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct GNUNET_HashCode coin_hash; - char *coin_ev; /* blinded message to be signed (in envelope) for each coin */ - size_t coin_ev_size; - - GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &coin_hash); - if (GNUNET_YES != - GNUNET_CRYPTO_rsa_blind (&coin_hash, - &fc->blinding_key.bks, - md->fresh_pks[i].rsa_public_key, - &coin_ev, - &coin_ev_size)) + struct TALER_PlanchetDetail pd; + + if (GNUNET_OK != + TALER_planchet_prepare (&md->fresh_pks[i], + fc, + &pd)) { /* This should have been noticed during the preparation stage. */ GNUNET_break (0); @@ -1322,9 +1296,9 @@ TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange, } GNUNET_assert (0 == json_array_append_new (tmp, - GNUNET_JSON_from_data (coin_ev, - coin_ev_size))); - GNUNET_free (coin_ev); + GNUNET_JSON_from_data (pd.coin_ev, + pd.coin_ev_size))); + GNUNET_free (pd.coin_ev); } GNUNET_assert (0 == json_array_append_new (coin_evs, @@ -1510,13 +1484,13 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshRevealHandle *rrh, struct TALER_DenominationPublicKey *pk; json_t *jsonai; struct GNUNET_CRYPTO_RsaSignature *blind_sig; - struct GNUNET_CRYPTO_RsaSignature *sig; struct TALER_CoinSpendPublicKeyP coin_pub; struct GNUNET_HashCode coin_hash; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig), GNUNET_JSON_spec_end() }; + struct TALER_FreshCoin coin; fc = &rrh->md->fresh_coins[rrh->noreveal_index][i]; pk = &rrh->md->fresh_pks[i]; @@ -1533,31 +1507,28 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshRevealHandle *rrh, return GNUNET_SYSERR; } - /* unblind the signature */ - sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, - &fc->blinding_key.bks, - pk->rsa_public_key); - GNUNET_CRYPTO_rsa_signature_free (blind_sig); - - /* verify the signature */ + /* 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, &coin_pub.eddsa_pub); GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), &coin_hash); - if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&coin_hash, - sig, - pk->rsa_public_key)) + TALER_planchet_to_coin (pk, + blind_sig, + fc, + &coin_hash, + &coin)) { GNUNET_break_op (0); - GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_CRYPTO_rsa_signature_free (blind_sig); GNUNET_JSON_parse_free (outer_spec); return GNUNET_SYSERR; } - coin_privs[i] = fc->coin_priv; - sigs[i].rsa_signature = sig; + GNUNET_CRYPTO_rsa_signature_free (blind_sig); + coin_privs[i] = coin.coin_priv; + sigs[i] = coin.sig; } GNUNET_JSON_parse_free (outer_spec); return GNUNET_OK; diff --git a/src/exchange-lib/exchange_api_reserve.c b/src/exchange-lib/exchange_api_reserve.c index 22e0e3d25..ef505d87d 100644 --- a/src/exchange-lib/exchange_api_reserve.c +++ b/src/exchange-lib/exchange_api_reserve.c @@ -693,9 +693,9 @@ struct TALER_EXCHANGE_ReserveWithdrawHandle TALER_EXCHANGE_ReserveWithdrawResultCallback cb; /** - * Key used to blind the value. + * Secrets of the planchet. */ - struct TALER_DenominationBlindingKeyP blinding_key; + struct TALER_PlanchetSecretsP ps; /** * Denomination key we are withdrawing. @@ -739,8 +739,7 @@ reserve_withdraw_ok (struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh, const json_t *json) { struct GNUNET_CRYPTO_RsaSignature *blind_sig; - struct GNUNET_CRYPTO_RsaSignature *sig; - struct TALER_DenominationSignature dsig; + struct TALER_FreshCoin fc; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig), GNUNET_JSON_spec_end() @@ -754,29 +753,28 @@ reserve_withdraw_ok (struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh, GNUNET_break_op (0); return GNUNET_SYSERR; } - sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, - &wsh->blinding_key.bks, - wsh->pk->key.rsa_public_key); - GNUNET_CRYPTO_rsa_signature_free (blind_sig); if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&wsh->c_hash, - sig, - wsh->pk->key.rsa_public_key)) + TALER_planchet_to_coin (&wsh->pk->key, + blind_sig, + &wsh->ps, + &wsh->c_hash, + &fc)) { GNUNET_break_op (0); - GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } + GNUNET_JSON_parse_free (spec); + /* signature is valid, return it to the application */ - dsig.rsa_signature = sig; wsh->cb (wsh->cb_cls, MHD_HTTP_OK, TALER_EC_NONE, - &dsig, + &fc.sig, json); /* make sure callback isn't called again after return */ wsh->cb = NULL; - GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature); return GNUNET_OK; } @@ -978,9 +976,7 @@ handle_reserve_withdraw_finished (void *cls, * @param exchange the exchange handle; the exchange must be ready to operate * @param pk kind of coin to create * @param reserve_priv private key of the reserve to withdraw from - * @param coin_priv where to fetch the coin's private key, - * caller must have committed this value to disk before the call (with @a pk) - * @param blinding_key where to fetch the coin's blinding key + * @param ps secrets of the planchet * caller must have committed this value to disk before the call (with @a pk) * @param res_cb the callback to call when the final result for this request is available * @param res_cb_cls closure for the above callback @@ -992,44 +988,33 @@ struct TALER_EXCHANGE_ReserveWithdrawHandle * TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange, const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKeyP *blinding_key, + const struct TALER_PlanchetSecretsP *ps, TALER_EXCHANGE_ReserveWithdrawResultCallback res_cb, void *res_cb_cls) { struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; struct TALER_WithdrawRequestPS req; struct TALER_ReserveSignatureP reserve_sig; - struct TALER_CoinSpendPublicKeyP coin_pub; struct GNUNET_CURL_Context *ctx; struct TALER_Amount amount_with_fee; - char *coin_ev; - size_t coin_ev_size; json_t *withdraw_obj; CURL *eh; + struct TALER_PlanchetDetail pd; + if (GNUNET_OK != + TALER_planchet_prepare (&pk->key, + ps, + &pd)) + { + GNUNET_break_op (0); + return NULL; + } wsh = GNUNET_new (struct TALER_EXCHANGE_ReserveWithdrawHandle); wsh->exchange = exchange; wsh->cb = res_cb; wsh->cb_cls = res_cb_cls; wsh->pk = pk; - - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &wsh->c_hash); - if (GNUNET_YES != - GNUNET_CRYPTO_rsa_blind (&wsh->c_hash, - &blinding_key->bks, - pk->key.rsa_public_key, - &coin_ev, - &coin_ev_size)) - { - GNUNET_break_op (0); - GNUNET_free (wsh); - return NULL; - } + wsh->c_hash = pd.c_hash; GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, &wsh->reserve_pub.eddsa_pub); req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); @@ -1042,7 +1027,7 @@ TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange, { /* exchange gave us denomination keys that overflow like this!? */ GNUNET_break_op (0); - GNUNET_free (coin_ev); + GNUNET_free (pd.coin_ev); GNUNET_free (wsh); return NULL; } @@ -1050,10 +1035,9 @@ TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange, &amount_with_fee); TALER_amount_hton (&req.withdraw_fee, &pk->fee_withdraw); - GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key, - &req.h_denomination_pub); - GNUNET_CRYPTO_hash (coin_ev, - coin_ev_size, + req.h_denomination_pub = pd.denom_pub_hash; + GNUNET_CRYPTO_hash (pd.coin_ev, + pd.coin_ev_size, &req.h_coin_envelope); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, @@ -1062,11 +1046,11 @@ TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange, withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub and coin_ev */ " s:o, s:o}",/* reserve_pub and reserve_sig */ "denom_pub", GNUNET_JSON_from_rsa_public_key (pk->key.rsa_public_key), - "coin_ev", GNUNET_JSON_from_data (coin_ev, - coin_ev_size), + "coin_ev", GNUNET_JSON_from_data (pd.coin_ev, + pd.coin_ev_size), "reserve_pub", GNUNET_JSON_from_data_auto (&wsh->reserve_pub), "reserve_sig", GNUNET_JSON_from_data_auto (&reserve_sig)); - GNUNET_free (coin_ev); + GNUNET_free (pd.coin_ev); if (NULL == withdraw_obj) { GNUNET_break (0); @@ -1074,7 +1058,7 @@ TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange, } - wsh->blinding_key = *blinding_key; + wsh->ps = *ps; wsh->url = MAH_path_to_url (exchange, "/reserve/withdraw"); eh = curl_easy_init (); diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 70fb3ffdc..7c0dfa982 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -341,14 +341,9 @@ struct Command struct TALER_DenominationSignature sig; /** - * Set (by the interpreter) to the coin's private key. + * Private key material of the coin, set by the interpreter. */ - struct TALER_CoinSpendPrivateKeyP coin_priv; - - /** - * Blinding key used for the operation. - */ - struct TALER_DenominationBlindingKeyP blinding_key; + struct TALER_PlanchetSecretsP ps; /** * Withdraw handle (while operation is running). @@ -1984,7 +1979,7 @@ get_public_key_from_coin_command (const struct Command *coin, switch (coin->oc) { case OC_WITHDRAW_SIGN: - GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv, + GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.ps.coin_priv.eddsa_priv, &coin_pub->eddsa_pub); break; case OC_REFRESH_REVEAL: @@ -2016,7 +2011,6 @@ interpreter_run (void *cls) struct Command *cmd = &is->commands[is->ip]; const struct Command *ref; struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_Amount amount; struct GNUNET_TIME_Absolute execution_date; json_t *sender_details; @@ -2165,27 +2159,14 @@ interpreter_run (void *cls) return; } - /* create coin's private key */ - { - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv; - GNUNET_free (priv); - } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &cmd->details.reserve_withdraw.blinding_key, - sizeof (cmd->details.reserve_withdraw.blinding_key)); + TALER_planchet_setup_random (&cmd->details.reserve_withdraw.ps); cmd->details.reserve_withdraw.wsh = TALER_EXCHANGE_reserve_withdraw (exchange, - cmd->details.reserve_withdraw.pk, - &ref->details.admin_add_incoming.reserve_priv, - &cmd->details.reserve_withdraw.coin_priv, - &cmd->details.reserve_withdraw.blinding_key, - &reserve_withdraw_cb, - is); + cmd->details.reserve_withdraw.pk, + &ref->details.admin_add_incoming.reserve_priv, + &cmd->details.reserve_withdraw.ps, + &reserve_withdraw_cb, + is); if (NULL == cmd->details.reserve_withdraw.wsh) { GNUNET_break (0); @@ -2217,7 +2198,7 @@ interpreter_run (void *cls) switch (ref->oc) { case OC_WITHDRAW_SIGN: - coin_priv = &ref->details.reserve_withdraw.coin_priv; + coin_priv = &ref->details.reserve_withdraw.ps.coin_priv; coin_pk = ref->details.reserve_withdraw.pk; coin_pk_sig = &ref->details.reserve_withdraw.sig; break; @@ -2376,7 +2357,7 @@ interpreter_run (void *cls) GNUNET_assert (NULL != ref); GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); - melt_priv = ref->details.reserve_withdraw.coin_priv; + melt_priv = ref->details.reserve_withdraw.ps.coin_priv; if (GNUNET_OK != TALER_string_to_amount (md->amount, &melt_amount)) @@ -2478,7 +2459,7 @@ interpreter_run (void *cls) /* finally, use private key from withdraw sign command */ cmd->details.refresh_link.rlh = TALER_EXCHANGE_refresh_link (exchange, - &ref->details.reserve_withdraw.coin_priv, + &ref->details.reserve_withdraw.ps.coin_priv, &link_cb, is); if (NULL == cmd->details.refresh_link.rlh) @@ -2745,8 +2726,7 @@ interpreter_run (void *cls) = TALER_EXCHANGE_payback (exchange, ref->details.reserve_withdraw.pk, &ref->details.reserve_withdraw.sig, - &ref->details.reserve_withdraw.coin_priv, - &ref->details.reserve_withdraw.blinding_key, + &ref->details.reserve_withdraw.ps, &payback_cb, is); return; diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 39de34b8a..d5024e6c8 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -396,7 +396,83 @@ int TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info); -/* ****************** Refresh crypto primitives ************* */ +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Header for serializations of coin-specific information about the + * fresh coins we generate. These are the secrets that arise during + * planchet generation, which is the first stage of creating a new + * coin. + */ +struct TALER_PlanchetSecretsP +{ + + /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** + * The blinding key. + */ + struct TALER_DenominationBlindingKeyP blinding_key; + +}; + +GNUNET_NETWORK_STRUCT_END + + +/** + * Details about a planchet that the customer wants to obtain + * a withdrawal authorization. This is the information that + * will need to be sent to the exchange to obtain the blind + * signature required to turn a planchet into a coin. + */ +struct TALER_PlanchetDetail +{ + /** + * Hash of the denomination public key. + */ + struct GNUNET_HashCode denom_pub_hash; + + /** + * Hash of the coin's public key. Kept around so we do not need to + * compute it again. Can be recomputed by hashing the public key + * of @a coin_priv if storage is at a premium. + */ + struct GNUNET_HashCode c_hash; + + /** + * Blinded coin (see GNUNET_CRYPTO_rsa_blind()). Note: is malloc()'ed! + */ + char *coin_ev; + + /** + * Number of bytes in @a coin_ev. + */ + size_t coin_ev_size; +}; + + +/** + * Information about a (fresh) coin, returned from the API when we + * finished creating a coin. Note that @e sig needs to be freed + * using the appropriate code. + */ +struct TALER_FreshCoin +{ + + /** + * The exchange's signature over the coin's public key. + */ + struct TALER_DenominationSignature sig; + + /** + * The coin's private key. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + +}; GNUNET_NETWORK_STRUCT_BEGIN @@ -426,6 +502,7 @@ struct TALER_TransferSecretP */ #define TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR "32" + /** * Raw value of a wire transfer subjects, without the checksum. */ @@ -472,6 +549,68 @@ struct TALER_WireTransferIdentifierP GNUNET_NETWORK_STRUCT_END + +/** + * Setup information for a fresh coin, deriving the coin private key + * and the blinding factor from the @a secret_seed with a KDF salted + * by the @a coin_num_salt. + * + * @param secret_seed seed to use for KDF to derive coin keys + * @param coin_num_salt number of the coin to include in KDF + * @param[out] fc value to initialize + */ +void +TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, + unsigned int coin_num_salt, + struct TALER_PlanchetSecretsP *fc); + + +/** + * Setup information for a fresh coin. + * + * @param[out] ps value to initialize + */ +void +TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps); + + +/** + * Prepare a planchet for tipping. Creates and blinds a coin. + * + * @param dk denomination key for the coin to be created + * @param ps secret planchet internals (for #TALER_planchet_to_coin) + * @param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and + * other withdraw operations + * @return #GNUNET_OK on success + */ +int +TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, + const struct TALER_PlanchetSecretsP *ps, + struct TALER_PlanchetDetail *pd); + + +/** + * Obtain a coin from the planchet's secrets and the blind signature + * of the exchange. + * + * @param dk denomination key, must match what was given to #TALER_planchet_prepare() + * @param blind_sig blind signature from the exchange + * @param ps secrets from #TALER_planchet_prepare() + * @param c_hash hash of the coin's public key for verification of the signature + * @param[out] coin set to the details of the fresh coin + * @return #GNUNET_OK on success + */ +int +TALER_planchet_to_coin (const struct TALER_DenominationPublicKey *dk, + const struct GNUNET_CRYPTO_RsaSignature *blind_sig, + const struct TALER_PlanchetSecretsP *ps, + const struct GNUNET_HashCode *c_hash, + struct TALER_FreshCoin *coin); + + +/* ****************** Refresh crypto primitives ************* */ + + /** * Given the coin and the transfer private keys, compute the * transfer secret. (Technically, we only need one of the two @@ -516,44 +655,5 @@ TALER_link_recover_transfer_secret (const struct TALER_TransferPublicKeyP *trans const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_TransferSecretP *transfer_secret); -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Header for serializations of coin-specific information about the - * fresh coins we generate during a melt. - */ -struct TALER_PlanchetSecretsP -{ - - /** - * Private key of the coin. - */ - struct TALER_CoinSpendPrivateKeyP coin_priv; - - /** - * The blinding key. - */ - struct TALER_DenominationBlindingKeyP blinding_key; - -}; - -GNUNET_NETWORK_STRUCT_END - - -/** - * Setup information for a fresh coin, deriving the coin private key - * and the blinding factor from the @a secret_seed with a KDF salted - * by the @a coin_num_salt. - * - * @param secret_seed seed to use for KDF to derive coin keys - * @param coin_num_salt number of the coin to include in KDF - * @param[out] fc value to initialize - */ -void -TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, - unsigned int coin_num_salt, - struct TALER_PlanchetSecretsP *fc); - - #endif diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 814078a02..3b4562a69 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -976,9 +976,7 @@ typedef void * @param exchange the exchange handle; the exchange must be ready to operate * @param pk kind of coin to create * @param reserve_priv private key of the reserve to withdraw from - * @param coin_priv where to fetch the coin's private key, - * caller must have committed this value to disk before the call (with @a pk) - * @param blinding_key where to fetch the coin's blinding key + * @param ps secrets of the planchet * caller must have committed this value to disk before the call (with @a pk) * @param res_cb the callback to call when the final result for this request is available * @param res_cb_cls closure for @a res_cb @@ -990,8 +988,7 @@ struct TALER_EXCHANGE_ReserveWithdrawHandle * TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange, const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKeyP *blinding_key, + const struct TALER_PlanchetSecretsP *ps, TALER_EXCHANGE_ReserveWithdrawResultCallback res_cb, void *res_cb_cls); @@ -1540,8 +1537,7 @@ typedef void * @param exchange the exchange handle; the exchange must be ready to operate * @param pk kind of coin to pay back * @param denom_sig signature over the coin by the exchange using @a pk - * @param coin_priv the coin's private key, - * @param blinding_key where to fetch the coin's blinding key + * @param ps secret internals of the original planchet * @param payback_cb the callback to call when the final result for this request is available * @param payback_cb_cls closure for @a payback_cb * @return NULL @@ -1552,8 +1548,7 @@ struct TALER_EXCHANGE_PaybackHandle * TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange, const struct TALER_EXCHANGE_DenomPublicKey *pk, const struct TALER_DenominationSignature *denom_sig, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKeyP *blinding_key, + const struct TALER_PlanchetSecretsP *ps, TALER_EXCHANGE_PaybackResultCallback payback_cb, void *payback_cb_cls); diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 34e07a351..84d4f5dee 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -134,6 +134,4 @@ const struct GNUNET_OS_ProjectData * TALER_project_data_default (void); - - #endif diff --git a/src/util/crypto.c b/src/util/crypto.c index 378b73dc4..efc74850d 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -170,24 +170,50 @@ TALER_link_recover_transfer_secret (const struct TALER_TransferPublicKeyP *trans } +/** + * Set the bits in the private EdDSA key so that they match + * the specification. + * + * @param[in,out] pk private key to patch + */ +static void +patch_private_key (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) +{ + uint8_t *p = (uint8_t *) pk; + + /* Taken from like 170-172 of libgcrypt/cipher/ecc.c + * We note that libgcrypt stores the private key in the reverse order + * from many Ed25519 implementatons. */ + p[0] &= 0x7f; /* Clear bit 255. */ + p[0] |= 0x40; /* Set bit 254. */ + p[31] &= 0xf8; /* Clear bits 2..0 so that d mod 8 == 0 */ + + /* FIXME: Run GNUNET_CRYPTO_ecdhe_key_create several times and inspect + * the output to verify that the same bits are set and cleared. + * Is it worth also adding a test case that runs gcry_pk_testkey on + * this key after first parsing it into libgcrypt's s-expression mess + * ala decode_private_eddsa_key from gnunet/src/util/crypto_ecc.c? + * It'd run check_secret_key but not test_keys from libgcrypt/cipher/ecc.c */ +} + + /** * Setup information for a fresh coin. * * @param secret_seed seed to use for KDF to derive coin keys * @param coin_num_salt number of the coin to include in KDF - * @param[out] fc value to initialize + * @param[out] ps value to initialize */ void TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, - unsigned int coin_num_salt, - struct TALER_PlanchetSecretsP *fc) + unsigned int coin_num_salt, + struct TALER_PlanchetSecretsP *ps) { uint32_t be_salt = htonl (coin_num_salt); - uint8_t *p; GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_kdf (fc, - sizeof (*fc), + GNUNET_CRYPTO_kdf (ps, + sizeof (*ps), &be_salt, sizeof (be_salt), secret_seed, @@ -195,24 +221,97 @@ TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, "taler-coin-derivation", strlen ("taler-coin-derivation"), NULL, 0)); + patch_private_key (&ps->coin_priv.eddsa_priv); +} - /* Taken from like 170-172 of libgcrypt/cipher/ecc.c - * We note that libgcrypt stores the private key in the reverse order - * from many Ed25519 implementatons. */ - p = (uint8_t *) &(fc->coin_priv); - p[0] &= 0x7f; /* Clear bit 255. */ - p[0] |= 0x40; /* Set bit 254. */ - p[31] &= 0xf8; /* Clear bits 2..0 so that d mod 8 == 0 */ - /* FIXME: Run GNUNET_CRYPTO_ecdhe_key_create several times and inspect - * the output to verify that the same bits are set and cleared. - * Is it worth also adding a test case that runs gcry_pk_testkey on - * this key after first parsing it into libgcrypt's s-expression mess - * ala decode_private_eddsa_key from gnunet/src/util/crypto_ecc.c? - * It'd run check_secret_key but not test_keys from libgcrypt/cipher/ecc.c */ +/** + * Setup information for a fresh coin. + * + * @param[out] ps value to initialize + */ +void +TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps) +{ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, + ps, + sizeof (*ps)); + patch_private_key (&ps->coin_priv.eddsa_priv); +} + + +/** + * Prepare a planchet for tipping. Creates and blinds a coin. + * + * @param dk denomination key for the coin to be created + * @param ps secret planchet internals (for #TALER_planchet_to_coin) + * @param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and + * other withdraw operations + * @return #GNUNET_OK on success + */ +int +TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, + const struct TALER_PlanchetSecretsP *ps, + struct TALER_PlanchetDetail *pd) +{ + struct TALER_CoinSpendPublicKeyP coin_pub; + + GNUNET_CRYPTO_eddsa_key_get_public (&ps->coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &pd->c_hash); + if (GNUNET_YES != + GNUNET_CRYPTO_rsa_blind (&pd->c_hash, + &ps->blinding_key.bks, + dk->rsa_public_key, + &pd->coin_ev, + &pd->coin_ev_size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_rsa_public_key_hash (dk->rsa_public_key, + &pd->denom_pub_hash); + return GNUNET_OK; } +/** + * Obtain a coin from the planchet's secrets and the blind signature + * of the exchange. + * + * @param dk denomination key, must match what was given to #TALER_planchet_prepare() + * @param blind_sig blind signature from the exchange + * @param ps secrets from #TALER_planchet_prepare() + * @param c_hash hash of the coin's public key for verification of the signature + * @param[out] coin set to the details of the fresh coin + * @return #GNUNET_OK on success + */ +int +TALER_planchet_to_coin (const struct TALER_DenominationPublicKey *dk, + const struct GNUNET_CRYPTO_RsaSignature *blind_sig, + const struct TALER_PlanchetSecretsP *ps, + const struct GNUNET_HashCode *c_hash, + struct TALER_FreshCoin *coin) +{ + struct GNUNET_CRYPTO_RsaSignature *sig; + sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, + &ps->blinding_key.bks, + dk->rsa_public_key); + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (c_hash, + sig, + dk->rsa_public_key)) + { + GNUNET_break_op (0); + GNUNET_CRYPTO_rsa_signature_free (sig); + return GNUNET_SYSERR; + } + coin->sig.rsa_signature = sig; + coin->coin_priv = ps->coin_priv; + return GNUNET_OK; +} /* end of crypto.c */ diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c index 953ad94df..4713b3a35 100644 --- a/src/util/test_crypto.c +++ b/src/util/test_crypto.c @@ -71,11 +71,11 @@ test_high_level () &secret2, sizeof (secret))); TALER_planchet_setup_refresh (&secret, - 0, - &fc1); + 0, + &fc1); TALER_planchet_setup_refresh (&secret, - 1, - &fc2); + 1, + &fc2); GNUNET_assert (0 != memcmp (&fc1, &fc2, @@ -84,12 +84,55 @@ test_high_level () } +/** + * Test the basic planchet functionality of creating a fresh planchet + * and extracting the respective signature. + * + * @return 0 on success + */ +static int +test_planchets () +{ + struct TALER_PlanchetSecretsP ps; + struct TALER_DenominationPrivateKey dk_priv; + struct TALER_DenominationPublicKey dk_pub; + struct TALER_PlanchetDetail pd; + struct GNUNET_CRYPTO_RsaSignature *blind_sig; + struct TALER_FreshCoin coin; + + dk_priv.rsa_private_key = GNUNET_CRYPTO_rsa_private_key_create (1024); + dk_pub.rsa_public_key = GNUNET_CRYPTO_rsa_private_key_get_public (dk_priv.rsa_private_key); + TALER_planchet_setup_random (&ps); + GNUNET_assert (GNUNET_OK == + TALER_planchet_prepare (&dk_pub, + &ps, + &pd)); + blind_sig = GNUNET_CRYPTO_rsa_sign_blinded (dk_priv.rsa_private_key, + pd.coin_ev, + pd.coin_ev_size); + GNUNET_assert (NULL != blind_sig); + GNUNET_assert (GNUNET_OK == + TALER_planchet_to_coin (&dk_pub, + blind_sig, + &ps, + &pd.c_hash, + &coin)); + GNUNET_CRYPTO_rsa_signature_free (blind_sig); + GNUNET_CRYPTO_rsa_signature_free (coin.sig.rsa_signature); + GNUNET_CRYPTO_rsa_private_key_free (dk_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_free (dk_pub.rsa_public_key); + return 0; +} + + int main(int argc, const char *const argv[]) { if (0 != test_high_level ()) return 1; + if (0 != test_planchets ()) + return 1; return 0; } -- cgit v1.2.3