summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-06-26 15:34:14 +0200
committerChristian Grothoff <christian@grothoff.org>2019-06-26 15:34:44 +0200
commit3a2f72b4aad3c2719e4326d30a97e49e26b5d797 (patch)
treec6930c9398872eac2a9968ad08ff7066cd88e9fb
parent30903b93866900e41bb4defa4d7b8ee49f3f3f99 (diff)
downloadexchange-3a2f72b4aad3c2719e4326d30a97e49e26b5d797.tar.gz
exchange-3a2f72b4aad3c2719e4326d30a97e49e26b5d797.tar.bz2
exchange-3a2f72b4aad3c2719e4326d30a97e49e26b5d797.zip
implement /link signatures
-rw-r--r--ChangeLog5
-rw-r--r--src/exchange/taler-exchange-httpd_keystate.c2
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_link.c9
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.c109
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c42
-rw-r--r--src/include/taler_error_codes.h7
-rw-r--r--src/include/taler_exchangedb_plugin.h14
-rw-r--r--src/include/taler_signatures.h46
-rw-r--r--src/lib/exchange_api_refresh.c32
9 files changed, 235 insertions, 31 deletions
diff --git a/ChangeLog b/ChangeLog
index 468fad8a..61ee3b6f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Wed 26 Jun 2019 03:31:52 PM CEST
+ Adding link signatures to prevent exchange from tracking
+ users using coins falsely believed to have been recovered via /link,
+ bumping protocol version to 4. -CG
+
Sat 08 Jun 2019 07:54:33 PM CEST
Change payto://sepa/ to payto://iban/ as per current spec. -CG
diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c
index 6134faf3..c4e55021 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -39,7 +39,7 @@
* release version, and the format is NOT the same that semantic
* versioning uses either.
*/
-#define TALER_PROTOCOL_VERSION "3:0:0"
+#define TALER_PROTOCOL_VERSION "4:0:0"
/**
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c b/src/exchange/taler-exchange-httpd_refresh_link.c
index 0ec505a8..04145f48 100644
--- a/src/exchange/taler-exchange-httpd_refresh_link.c
+++ b/src/exchange/taler-exchange-httpd_refresh_link.c
@@ -92,6 +92,9 @@ handle_link_data (void *cls,
json_object_set_new (obj,
"ev_sig",
GNUNET_JSON_from_rsa_signature (pos->ev_sig.rsa_signature));
+ json_object_set_new (obj,
+ "link_sig",
+ GNUNET_JSON_from_data_auto (&pos->orig_coin_link_sig));
if (0 !=
json_array_append_new (list,
obj))
@@ -204,9 +207,9 @@ TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
"run link",
- &mhd_ret,
- &refresh_link_transaction,
- &ctx))
+ &mhd_ret,
+ &refresh_link_transaction,
+ &ctx))
{
if (NULL != ctx.mlist)
json_decref (ctx.mlist);
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c
index 6fc8d1c5..2f6d0b14 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -145,6 +145,12 @@ struct RevealContext
const struct TALER_RefreshCoinData *rcds;
/**
+ * Signatures over the link data (of type
+ * #TALER_SIGNATURE_WALLET_COIN_LINK)
+ */
+ const struct TALER_CoinSpendSignatureP *link_sigs;
+
+ /**
* Envelopes with the signatures to be returned. Initially NULL.
*/
struct TALER_DenominationSignature *ev_sigs;
@@ -491,6 +497,7 @@ refresh_reveal_persist (void *cls,
struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
rrc->denom_pub = rctx->dkis[i]->denom_pub;
+ rrc->orig_coin_link_sig = rctx->link_sigs[i];
rrc->coin_ev = rctx->rcds[i].coin_ev;
rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
rrc->coin_sig = rctx->ev_sigs[i];
@@ -524,6 +531,7 @@ refresh_reveal_persist (void *cls,
* @param rctx context for the operation, partially built at this time
* @param transfer_pub transfer public key
* @param tp_json private transfer keys in JSON format
+ * @param link_sigs_json link signatures in JSON format
* @param new_denoms_h_json requests for fresh coins to be created
* @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code
@@ -532,12 +540,14 @@ static int
handle_refresh_reveal_json (struct MHD_Connection *connection,
struct RevealContext *rctx,
const json_t *tp_json,
+ const json_t *link_sigs_json,
const json_t *new_denoms_h_json,
const json_t *coin_evs)
{
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
unsigned int num_tprivs = json_array_size (tp_json);
struct TEH_KS_StateHandle *key_state;
+ struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1);
if ( (num_fresh_coins >= MAX_FRESH_COINS) ||
@@ -545,7 +555,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
{
GNUNET_break_op (0);
return TEH_RESPONSE_reply_arg_invalid (connection,
- TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
+ TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
"new_denoms");
}
@@ -557,6 +567,14 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
"new_denoms/coin_evs");
}
+ if (json_array_size (new_denoms_h_json) !=
+ json_array_size (link_sigs_json))
+ {
+ GNUNET_break_op (0);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
+ "new_denoms/link_sigs");
+ }
/* Parse transfer private keys array */
for (unsigned int i=0;i<num_tprivs;i++)
@@ -579,7 +597,9 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
/* Resolve denomination hashes */
{
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins];
+ struct GNUNET_HashCode dki_h[num_fresh_coins];
struct TALER_RefreshCoinData rcds[num_fresh_coins];
+ struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
int res;
/* Resolve denomination hashes */
@@ -596,10 +616,9 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
/* Parse denomination key hashes */
for (unsigned int i=0;i<num_fresh_coins;i++)
{
- struct GNUNET_HashCode dpk_h;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto (NULL,
- &dpk_h),
+ &dki_h[i]),
GNUNET_JSON_spec_end ()
};
@@ -614,7 +633,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state,
- &dpk_h,
+ &dki_h[i],
TEH_KS_DKU_WITHDRAW);
if (NULL == dkis[i])
{
@@ -652,9 +671,85 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
rcd->dk = &dkis[i]->denom_pub;
}
+ /* lookup old_coin_pub in database */
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ (qs = TEH_plugin->get_melt (TEH_plugin->cls,
+ NULL,
+ &rctx->rc,
+ &refresh_melt)))
+ {
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ res = TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
+ "rc");
+ break;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ res = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ default:
+ GNUNET_break (0); /* should be impossible */
+ res = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_INTERNAL_INVARIANT_FAILURE);
+ break;
+ }
+ goto cleanup;
+ }
+ }
+ /* Parse link signatures array */
+ for (unsigned int i=0;i<num_fresh_coins;i++)
+ {
+ struct GNUNET_JSON_Specification link_spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, &link_sigs[i]),
+ GNUNET_JSON_spec_end ()
+ };
+ int res;
+
+ res = TEH_PARSE_json_array (connection,
+ link_sigs_json,
+ link_spec,
+ i,
+ -1);
+ if (GNUNET_OK != res)
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ /* Check link_sigs[i] signature */
+ {
+ struct TALER_LinkDataPS ldp;
+
+ ldp.purpose.size = htonl (sizeof (ldp));
+ ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
+ ldp.h_denom_pub = dki_h[i];
+ ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
+ ldp.transfer_pub = rctx->gamma_tp;
+ GNUNET_CRYPTO_hash (rcds[i].coin_ev,
+ rcds[i].coin_ev_size,
+ &ldp.coin_envelope_hash);
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
+ &ldp.purpose,
+ &link_sigs[i].eddsa_signature,
+ &refresh_melt.session.coin.coin_pub.eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ res = TEH_RESPONSE_reply_signature_invalid (connection,
+ TALER_EC_REFRESH_REVEAL_LINK_SIGNATURE_INVALID,
+ "link_sig");
+ goto cleanup;
+ }
+ }
+ }
+
rctx->num_fresh_coins = num_fresh_coins;
rctx->rcds = rcds;
rctx->dkis = dkis;
+ rctx->link_sigs = link_sigs;
/* sign _early_ (optimistic!) to keep out of transaction scope! */
rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins,
@@ -749,7 +844,6 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
}
-
/**
* Handle a "/refresh/reveal" request. This time, the client reveals
* the private transfer keys except for the cut-and-choose value
@@ -777,12 +871,14 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
json_t *root;
json_t *coin_evs;
json_t *transfer_privs;
+ json_t *link_sigs;
json_t *new_denoms_h;
struct RevealContext rctx;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc),
GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
+ GNUNET_JSON_spec_json ("link_sigs", &link_sigs),
GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h),
GNUNET_JSON_spec_end ()
@@ -819,12 +915,13 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
GNUNET_JSON_parse_free (spec);
GNUNET_break_op (0);
return TEH_RESPONSE_reply_arg_invalid (connection,
- TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
+ TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
"transfer_privs");
}
res = handle_refresh_reveal_json (connection,
&rctx,
transfer_privs,
+ link_sigs,
new_denoms_h,
coin_evs);
GNUNET_JSON_parse_free (spec);
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 4945d5a5..9f0f044b 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -315,6 +315,7 @@ postgres_create_tables (void *cls)
GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_revealed_coins "
"(rc BYTEA NOT NULL REFERENCES refresh_commitments (rc) ON DELETE CASCADE"
",newcoin_index INT4 NOT NULL"
+ ",link_sig BYTEA NOT NULL CHECK(LENGTH(link_sig)=64)"
",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE"
",coin_ev BYTEA NOT NULL"
",ev_sig BYTEA NOT NULL"
@@ -951,18 +952,20 @@ postgres_prepare (PGconn *db_conn)
"INSERT INTO refresh_revealed_coins "
"(rc "
",newcoin_index "
+ ",link_sig "
",denom_pub_hash "
",coin_ev"
",ev_sig"
") VALUES "
- "($1, $2, $3, $4, $5);",
- 5),
+ "($1, $2, $3, $4, $5, $6);",
+ 6),
/* Obtain information about the coins created in a refresh
operation, used in #postgres_get_refresh_reveal() */
GNUNET_PQ_make_prepare ("get_refresh_revealed_coins",
"SELECT "
" newcoin_index"
",denom.denom_pub"
+ ",link_sig"
",coin_ev"
",ev_sig"
" FROM refresh_revealed_coins"
@@ -1239,6 +1242,7 @@ postgres_prepare (PGconn *db_conn)
" tp.transfer_pub"
",denoms.denom_pub"
",rrc.ev_sig"
+ ",rrc.link_sig"
" FROM refresh_commitments"
" JOIN refresh_revealed_coins rrc"
" USING (rc)"
@@ -3641,7 +3645,7 @@ postgres_select_refunds_by_coin (void *cls,
* Lookup refresh melt commitment data under the given @a rc.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database handle to use
+ * @param session database handle to use, NULL if not run in any transaction
* @param rc commitment hash to use to locate the operation
* @param[out] refresh_melt where to store the result
* @return transaction status
@@ -3652,6 +3656,7 @@ postgres_get_melt (void *cls,
const struct TALER_RefreshCommitmentP *rc,
struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
{
+ struct PostgresClosure *pc = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (rc),
GNUNET_PQ_query_param_end
@@ -3675,6 +3680,8 @@ postgres_get_melt (void *cls,
};
enum GNUNET_DB_QueryStatus qs;
+ if (NULL == session)
+ session = postgres_get_session (pc);
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_melt",
params,
@@ -3790,6 +3797,7 @@ postgres_insert_refresh_reveal (void *cls,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (rc),
GNUNET_PQ_query_param_uint32 (&i),
+ GNUNET_PQ_query_param_auto_from_type (&rrc->orig_coin_link_sig),
GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
GNUNET_PQ_query_param_fixed_size (rrc->coin_ev,
rrc->coin_ev_size),
@@ -3876,6 +3884,8 @@ add_revealed_coins (void *cls,
&off),
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
&rrc->denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_auto_from_type ("link_sig",
+ &rrc->orig_coin_link_sig),
GNUNET_PQ_result_spec_variable_size ("coin_ev",
(void **) &rrc->coin_ev,
&rrc->coin_ev_size),
@@ -3932,7 +3942,7 @@ postgres_get_refresh_reveal (void *cls,
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
- &tp),
+ &tp),
GNUNET_PQ_result_spec_variable_size ("transfer_privs",
&tpriv,
&tpriv_size),
@@ -4087,8 +4097,8 @@ free_link_data_list (void *cls,
*/
static void
add_ldl (void *cls,
- PGresult *result,
- unsigned int num_results)
+ PGresult *result,
+ unsigned int num_results)
{
struct LinkDataContext *ldctx = cls;
@@ -4102,11 +4112,13 @@ add_ldl (void *cls,
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
&transfer_pub),
- GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
- &pos->ev_sig.rsa_signature),
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
- &pos->denom_pub.rsa_public_key),
- GNUNET_PQ_result_spec_end
+ GNUNET_PQ_result_spec_auto_from_type ("link_sig",
+ &pos->orig_coin_link_sig),
+ GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
+ &pos->ev_sig.rsa_signature),
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+ &pos->denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
@@ -4173,10 +4185,10 @@ postgres_get_link_data (void *cls,
ldctx.last = NULL;
ldctx.status = GNUNET_OK;
qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
- "get_link",
- params,
- &add_ldl,
- &ldctx);
+ "get_link",
+ params,
+ &add_ldl,
+ &ldctx);
if (NULL != ldctx.last)
{
if (GNUNET_OK == ldctx.status)
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index fbd98352..5767a73b 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -617,7 +617,14 @@ enum TALER_ErrorCode
*/
TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND = 1361,
+ /**
+ * The signature made with the coin over the link data is invalid.
+ * This response is provided with HTTP status code
+ * MHD_HTTP_BAD_REQUEST.
+ */
+ TALER_EC_REFRESH_REVEAL_LINK_SIGNATURE_INVALID = 1362,
+
/**
* The coin specified in the link request is unknown to the exchange.
* This response is provided with HTTP status code
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 6f1625dd..67ebc62f 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -541,6 +541,12 @@ struct TALER_EXCHANGEDB_LinkDataList
* Signature over the blinded envelope.
*/
struct TALER_DenominationSignature ev_sig;
+
+ /**
+ * Signature of the original coin being refreshed over the
+ * link data, of type #TALER_SIGNATURE_WALLET_COIN_LINK
+ */
+ struct TALER_CoinSpendSignatureP orig_coin_link_sig;
};
@@ -794,6 +800,12 @@ struct TALER_EXCHANGEDB_RefreshRevealedCoin
struct TALER_DenominationPublicKey denom_pub;
/**
+ * Signature of the original coin being refreshed over the
+ * link data, of type #TALER_SIGNATURE_WALLET_COIN_LINK
+ */
+ struct TALER_CoinSpendSignatureP orig_coin_link_sig;
+
+ /**
* Blinded message to be signed (in envelope), with @e coin_env_size bytes.
*/
char *coin_ev;
@@ -1634,7 +1646,7 @@ struct TALER_EXCHANGEDB_Plugin
/**
- * Lookup refresh metl commitment data under the given @a rc.
+ * Lookup refresh melt commitment data under the given @a rc.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database handle to use
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index bff73f73..b738e315 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -130,9 +130,9 @@
#define TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED 1040
-/*********************/
-/* Wallet signatures */
-/*********************/
+/**********************/
+/* Auditor signatures */
+/**********************/
/**
* Signature where the auditor confirms that he is
@@ -209,6 +209,11 @@
*/
#define TALER_SIGNATURE_WALLET_COIN_PAYBACK 1203
+/**
+ * Signature using a coin key authenticating link data.
+ */
+#define TALER_SIGNATURE_WALLET_COIN_LINK 1204
+
/*******************/
/* Test signatures */
@@ -229,6 +234,41 @@
GNUNET_NETWORK_STRUCT_BEGIN
/**
+ * @brief Format used for to allow the wallet to authenticate
+ * link data provided by the exchange.
+ */
+struct TALER_LinkDataPS
+{
+
+ /**
+ * Purpose must be #TALER_SIGNATURE_WALLET_COIN_LINK.
+ * Used with an EdDSA signature of a `struct TALER_CoinPublicKeyP`.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash of the denomination public key of the new coin.
+ */
+ struct GNUNET_HashCode h_denom_pub;
+
+ /**
+ * Public key of the old coin being refreshed.
+ */
+ struct TALER_CoinSpendPublicKeyP old_coin_pub;
+
+ /**
+ * Transfer public key (for which the private key was not revealed)
+ */
+ struct TALER_TransferPublicKeyP transfer_pub;
+
+ /**
+ * Hash of the blinded new coin.
+ */
+ struct GNUNET_HashCode coin_envelope_hash;
+};
+
+
+/**
* @brief Format used for to generate the signature on a request to withdraw
* coins from a reserve.
*/
diff --git a/src/lib/exchange_api_refresh.c b/src/lib/exchange_api_refresh.c
index e2a3a245..61bee6d6 100644
--- a/src/lib/exchange_api_refresh.c
+++ b/src/lib/exchange_api_refresh.c
@@ -1065,7 +1065,7 @@ handle_refresh_melt_finished (void *cls,
{
rmh->melt_cb (rmh->melt_cb_cls,
response_code,
- TALER_JSON_get_error_code (j),
+ TALER_JSON_get_error_code (j),
noreveal_index,
(0 == response_code) ? NULL : &exchange_pub,
j);
@@ -1534,6 +1534,7 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
json_t *new_denoms_h;
json_t *coin_evs;
json_t *reveal_obj;
+ json_t *link_sigs;
CURL *eh;
struct GNUNET_CURL_Context *ctx;
struct MeltData *md;
@@ -1565,6 +1566,7 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
/* 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++)
{
struct GNUNET_HashCode denom_hash;
@@ -1591,6 +1593,30 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
json_array_append_new (coin_evs,
GNUNET_JSON_from_data (pd.coin_ev,
pd.coin_ev_size)));
+
+ /* compute link signature */
+ {
+ struct TALER_CoinSpendSignatureP link_sig;
+ struct TALER_LinkDataPS ldp;
+
+ ldp.purpose.size = htonl (sizeof (ldp));
+ ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
+ ldp.h_denom_pub = denom_hash;
+ GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
+ &ldp.old_coin_pub.eddsa_pub);
+ ldp.transfer_pub = transfer_pub;
+ GNUNET_CRYPTO_hash (pd.coin_ev,
+ pd.coin_ev_size,
+ &ldp.coin_envelope_hash);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv,
+ &ldp.purpose,
+ &link_sig.eddsa_signature));
+ GNUNET_assert (0 ==
+ json_array_append_new (link_sigs,
+ GNUNET_JSON_from_data_auto (&link_sig)));
+ }
+
GNUNET_free (pd.coin_ev);
}
@@ -1610,13 +1636,15 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
}
/* build main JSON request */
- reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}",
+ reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
"rc",
GNUNET_JSON_from_data_auto (&md->rc),
"transfer_pub",
GNUNET_JSON_from_data_auto (&transfer_pub),
"transfer_privs",
transfer_privs,
+ "link_sigs",
+ link_sigs,
"new_denoms_h",
new_denoms_h,
"coin_evs",