implement /link signatures

This commit is contained in:
Christian Grothoff 2019-06-26 15:34:14 +02:00
parent 30903b9386
commit 3a2f72b4aa
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
9 changed files with 235 additions and 31 deletions

View File

@ -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 Sat 08 Jun 2019 07:54:33 PM CEST
Change payto://sepa/ to payto://iban/ as per current spec. -CG Change payto://sepa/ to payto://iban/ as per current spec. -CG

View File

@ -39,7 +39,7 @@
* release version, and the format is NOT the same that semantic * release version, and the format is NOT the same that semantic
* versioning uses either. * versioning uses either.
*/ */
#define TALER_PROTOCOL_VERSION "3:0:0" #define TALER_PROTOCOL_VERSION "4:0:0"
/** /**

View File

@ -92,6 +92,9 @@ handle_link_data (void *cls,
json_object_set_new (obj, json_object_set_new (obj,
"ev_sig", "ev_sig",
GNUNET_JSON_from_rsa_signature (pos->ev_sig.rsa_signature)); 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 != if (0 !=
json_array_append_new (list, json_array_append_new (list,
obj)) obj))
@ -204,9 +207,9 @@ TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
if (GNUNET_OK != if (GNUNET_OK !=
TEH_DB_run_transaction (connection, TEH_DB_run_transaction (connection,
"run link", "run link",
&mhd_ret, &mhd_ret,
&refresh_link_transaction, &refresh_link_transaction,
&ctx)) &ctx))
{ {
if (NULL != ctx.mlist) if (NULL != ctx.mlist)
json_decref (ctx.mlist); json_decref (ctx.mlist);

View File

@ -144,6 +144,12 @@ struct RevealContext
*/ */
const struct TALER_RefreshCoinData *rcds; 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. * Envelopes with the signatures to be returned. Initially NULL.
*/ */
@ -491,6 +497,7 @@ refresh_reveal_persist (void *cls,
struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
rrc->denom_pub = rctx->dkis[i]->denom_pub; 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 = rctx->rcds[i].coin_ev;
rrc->coin_ev_size = rctx->rcds[i].coin_ev_size; rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
rrc->coin_sig = rctx->ev_sigs[i]; 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 rctx context for the operation, partially built at this time
* @param transfer_pub transfer public key * @param transfer_pub transfer public key
* @param tp_json private transfer keys in JSON format * @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 new_denoms_h_json requests for fresh coins to be created
* @param coin_evs envelopes of gamma-selected coins to be signed * @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code * @return MHD result code
@ -532,12 +540,14 @@ static int
handle_refresh_reveal_json (struct MHD_Connection *connection, handle_refresh_reveal_json (struct MHD_Connection *connection,
struct RevealContext *rctx, struct RevealContext *rctx,
const json_t *tp_json, const json_t *tp_json,
const json_t *link_sigs_json,
const json_t *new_denoms_h_json, const json_t *new_denoms_h_json,
const json_t *coin_evs) const json_t *coin_evs)
{ {
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
unsigned int num_tprivs = json_array_size (tp_json); unsigned int num_tprivs = json_array_size (tp_json);
struct TEH_KS_StateHandle *key_state; struct TEH_KS_StateHandle *key_state;
struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1); GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1);
if ( (num_fresh_coins >= MAX_FRESH_COINS) || if ( (num_fresh_coins >= MAX_FRESH_COINS) ||
@ -545,7 +555,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return TEH_RESPONSE_reply_arg_invalid (connection, 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"); "new_denoms");
} }
@ -557,6 +567,14 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH, TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
"new_denoms/coin_evs"); "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 */ /* Parse transfer private keys array */
for (unsigned int i=0;i<num_tprivs;i++) for (unsigned int i=0;i<num_tprivs;i++)
@ -579,7 +597,9 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
/* Resolve denomination hashes */ /* Resolve denomination hashes */
{ {
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins]; 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_RefreshCoinData rcds[num_fresh_coins];
struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
int res; int res;
/* Resolve denomination hashes */ /* Resolve denomination hashes */
@ -596,10 +616,9 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
/* Parse denomination key hashes */ /* Parse denomination key hashes */
for (unsigned int i=0;i<num_fresh_coins;i++) for (unsigned int i=0;i<num_fresh_coins;i++)
{ {
struct GNUNET_HashCode dpk_h;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto (NULL, GNUNET_JSON_spec_fixed_auto (NULL,
&dpk_h), &dki_h[i]),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -614,7 +633,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
return (GNUNET_NO == res) ? MHD_YES : MHD_NO; return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
} }
dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state, dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state,
&dpk_h, &dki_h[i],
TEH_KS_DKU_WITHDRAW); TEH_KS_DKU_WITHDRAW);
if (NULL == dkis[i]) if (NULL == dkis[i])
{ {
@ -652,9 +671,85 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
rcd->dk = &dkis[i]->denom_pub; 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->num_fresh_coins = num_fresh_coins;
rctx->rcds = rcds; rctx->rcds = rcds;
rctx->dkis = dkis; rctx->dkis = dkis;
rctx->link_sigs = link_sigs;
/* sign _early_ (optimistic!) to keep out of transaction scope! */ /* sign _early_ (optimistic!) to keep out of transaction scope! */
rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins, 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 * Handle a "/refresh/reveal" request. This time, the client reveals
* the private transfer keys except for the cut-and-choose value * 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 *root;
json_t *coin_evs; json_t *coin_evs;
json_t *transfer_privs; json_t *transfer_privs;
json_t *link_sigs;
json_t *new_denoms_h; json_t *new_denoms_h;
struct RevealContext rctx; struct RevealContext rctx;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc), GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc),
GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp), GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs), 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 ("coin_evs", &coin_evs),
GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h), GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
@ -819,12 +915,13 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
GNUNET_break_op (0); GNUNET_break_op (0);
return TEH_RESPONSE_reply_arg_invalid (connection, 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"); "transfer_privs");
} }
res = handle_refresh_reveal_json (connection, res = handle_refresh_reveal_json (connection,
&rctx, &rctx,
transfer_privs, transfer_privs,
link_sigs,
new_denoms_h, new_denoms_h,
coin_evs); coin_evs);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);

View File

@ -315,6 +315,7 @@ postgres_create_tables (void *cls)
GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_revealed_coins " GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_revealed_coins "
"(rc BYTEA NOT NULL REFERENCES refresh_commitments (rc) ON DELETE CASCADE" "(rc BYTEA NOT NULL REFERENCES refresh_commitments (rc) ON DELETE CASCADE"
",newcoin_index INT4 NOT NULL" ",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" ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE"
",coin_ev BYTEA NOT NULL" ",coin_ev BYTEA NOT NULL"
",ev_sig BYTEA NOT NULL" ",ev_sig BYTEA NOT NULL"
@ -951,18 +952,20 @@ postgres_prepare (PGconn *db_conn)
"INSERT INTO refresh_revealed_coins " "INSERT INTO refresh_revealed_coins "
"(rc " "(rc "
",newcoin_index " ",newcoin_index "
",link_sig "
",denom_pub_hash " ",denom_pub_hash "
",coin_ev" ",coin_ev"
",ev_sig" ",ev_sig"
") VALUES " ") VALUES "
"($1, $2, $3, $4, $5);", "($1, $2, $3, $4, $5, $6);",
5), 6),
/* Obtain information about the coins created in a refresh /* Obtain information about the coins created in a refresh
operation, used in #postgres_get_refresh_reveal() */ operation, used in #postgres_get_refresh_reveal() */
GNUNET_PQ_make_prepare ("get_refresh_revealed_coins", GNUNET_PQ_make_prepare ("get_refresh_revealed_coins",
"SELECT " "SELECT "
" newcoin_index" " newcoin_index"
",denom.denom_pub" ",denom.denom_pub"
",link_sig"
",coin_ev" ",coin_ev"
",ev_sig" ",ev_sig"
" FROM refresh_revealed_coins" " FROM refresh_revealed_coins"
@ -1239,6 +1242,7 @@ postgres_prepare (PGconn *db_conn)
" tp.transfer_pub" " tp.transfer_pub"
",denoms.denom_pub" ",denoms.denom_pub"
",rrc.ev_sig" ",rrc.ev_sig"
",rrc.link_sig"
" FROM refresh_commitments" " FROM refresh_commitments"
" JOIN refresh_revealed_coins rrc" " JOIN refresh_revealed_coins rrc"
" USING (rc)" " USING (rc)"
@ -3641,7 +3645,7 @@ postgres_select_refunds_by_coin (void *cls,
* Lookup refresh melt commitment data under the given @a rc. * Lookup refresh melt commitment data under the given @a rc.
* *
* @param cls the `struct PostgresClosure` with the plugin-specific state * @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 rc commitment hash to use to locate the operation
* @param[out] refresh_melt where to store the result * @param[out] refresh_melt where to store the result
* @return transaction status * @return transaction status
@ -3652,6 +3656,7 @@ postgres_get_melt (void *cls,
const struct TALER_RefreshCommitmentP *rc, const struct TALER_RefreshCommitmentP *rc,
struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt) struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
{ {
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (rc), GNUNET_PQ_query_param_auto_from_type (rc),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
@ -3675,6 +3680,8 @@ postgres_get_melt (void *cls,
}; };
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
if (NULL == session)
session = postgres_get_session (pc);
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_melt", "get_melt",
params, params,
@ -3790,6 +3797,7 @@ postgres_insert_refresh_reveal (void *cls,
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (rc), GNUNET_PQ_query_param_auto_from_type (rc),
GNUNET_PQ_query_param_uint32 (&i), 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_auto_from_type (&denom_pub_hash),
GNUNET_PQ_query_param_fixed_size (rrc->coin_ev, GNUNET_PQ_query_param_fixed_size (rrc->coin_ev,
rrc->coin_ev_size), rrc->coin_ev_size),
@ -3876,6 +3884,8 @@ add_revealed_coins (void *cls,
&off), &off),
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
&rrc->denom_pub.rsa_public_key), &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", GNUNET_PQ_result_spec_variable_size ("coin_ev",
(void **) &rrc->coin_ev, (void **) &rrc->coin_ev,
&rrc->coin_ev_size), &rrc->coin_ev_size),
@ -3932,7 +3942,7 @@ postgres_get_refresh_reveal (void *cls,
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
&tp), &tp),
GNUNET_PQ_result_spec_variable_size ("transfer_privs", GNUNET_PQ_result_spec_variable_size ("transfer_privs",
&tpriv, &tpriv,
&tpriv_size), &tpriv_size),
@ -4087,8 +4097,8 @@ free_link_data_list (void *cls,
*/ */
static void static void
add_ldl (void *cls, add_ldl (void *cls,
PGresult *result, PGresult *result,
unsigned int num_results) unsigned int num_results)
{ {
struct LinkDataContext *ldctx = cls; struct LinkDataContext *ldctx = cls;
@ -4102,11 +4112,13 @@ add_ldl (void *cls,
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
&transfer_pub), &transfer_pub),
GNUNET_PQ_result_spec_rsa_signature ("ev_sig", GNUNET_PQ_result_spec_auto_from_type ("link_sig",
&pos->ev_sig.rsa_signature), &pos->orig_coin_link_sig),
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
&pos->denom_pub.rsa_public_key), &pos->ev_sig.rsa_signature),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
&pos->denom_pub.rsa_public_key),
GNUNET_PQ_result_spec_end
}; };
if (GNUNET_OK != if (GNUNET_OK !=
@ -4173,10 +4185,10 @@ postgres_get_link_data (void *cls,
ldctx.last = NULL; ldctx.last = NULL;
ldctx.status = GNUNET_OK; ldctx.status = GNUNET_OK;
qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
"get_link", "get_link",
params, params,
&add_ldl, &add_ldl,
&ldctx); &ldctx);
if (NULL != ldctx.last) if (NULL != ldctx.last)
{ {
if (GNUNET_OK == ldctx.status) if (GNUNET_OK == ldctx.status)

View File

@ -617,6 +617,13 @@ enum TALER_ErrorCode
*/ */
TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND = 1361, 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. * The coin specified in the link request is unknown to the exchange.

View File

@ -541,6 +541,12 @@ struct TALER_EXCHANGEDB_LinkDataList
* Signature over the blinded envelope. * Signature over the blinded envelope.
*/ */
struct TALER_DenominationSignature ev_sig; 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;
}; };
@ -793,6 +799,12 @@ struct TALER_EXCHANGEDB_RefreshRevealedCoin
*/ */
struct TALER_DenominationPublicKey denom_pub; 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. * Blinded message to be signed (in envelope), with @e coin_env_size bytes.
*/ */
@ -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 cls the @e cls of this struct with the plugin-specific state
* @param session database handle to use * @param session database handle to use

View File

@ -130,9 +130,9 @@
#define TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED 1040 #define TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED 1040
/*********************/ /**********************/
/* Wallet signatures */ /* Auditor signatures */
/*********************/ /**********************/
/** /**
* Signature where the auditor confirms that he is * Signature where the auditor confirms that he is
@ -209,6 +209,11 @@
*/ */
#define TALER_SIGNATURE_WALLET_COIN_PAYBACK 1203 #define TALER_SIGNATURE_WALLET_COIN_PAYBACK 1203
/**
* Signature using a coin key authenticating link data.
*/
#define TALER_SIGNATURE_WALLET_COIN_LINK 1204
/*******************/ /*******************/
/* Test signatures */ /* Test signatures */
@ -228,6 +233,41 @@
GNUNET_NETWORK_STRUCT_BEGIN 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 * @brief Format used for to generate the signature on a request to withdraw
* coins from a reserve. * coins from a reserve.

View File

@ -1065,7 +1065,7 @@ handle_refresh_melt_finished (void *cls,
{ {
rmh->melt_cb (rmh->melt_cb_cls, rmh->melt_cb (rmh->melt_cb_cls,
response_code, response_code,
TALER_JSON_get_error_code (j), TALER_JSON_get_error_code (j),
noreveal_index, noreveal_index,
(0 == response_code) ? NULL : &exchange_pub, (0 == response_code) ? NULL : &exchange_pub,
j); j);
@ -1534,6 +1534,7 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
json_t *new_denoms_h; json_t *new_denoms_h;
json_t *coin_evs; json_t *coin_evs;
json_t *reveal_obj; json_t *reveal_obj;
json_t *link_sigs;
CURL *eh; CURL *eh;
struct GNUNET_CURL_Context *ctx; struct GNUNET_CURL_Context *ctx;
struct MeltData *md; struct MeltData *md;
@ -1565,6 +1566,7 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
/* now new_denoms */ /* now new_denoms */
GNUNET_assert (NULL != (new_denoms_h = json_array ())); GNUNET_assert (NULL != (new_denoms_h = json_array ()));
GNUNET_assert (NULL != (coin_evs = 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 GNUNET_HashCode denom_hash; struct GNUNET_HashCode denom_hash;
@ -1591,6 +1593,30 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
json_array_append_new (coin_evs, json_array_append_new (coin_evs,
GNUNET_JSON_from_data (pd.coin_ev, GNUNET_JSON_from_data (pd.coin_ev,
pd.coin_ev_size))); 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); GNUNET_free (pd.coin_ev);
} }
@ -1610,13 +1636,15 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
} }
/* build main JSON request */ /* 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", "rc",
GNUNET_JSON_from_data_auto (&md->rc), GNUNET_JSON_from_data_auto (&md->rc),
"transfer_pub", "transfer_pub",
GNUNET_JSON_from_data_auto (&transfer_pub), GNUNET_JSON_from_data_auto (&transfer_pub),
"transfer_privs", "transfer_privs",
transfer_privs, transfer_privs,
"link_sigs",
link_sigs,
"new_denoms_h", "new_denoms_h",
new_denoms_h, new_denoms_h,
"coin_evs", "coin_evs",