gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: -correctly implement CS idempote


From: gnunet
Subject: [taler-exchange] branch master updated: -correctly implement CS idempotency check on withdraw
Date: Tue, 15 Feb 2022 17:07:16 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new ef938e0f -correctly implement CS idempotency check on withdraw
ef938e0f is described below

commit ef938e0f7aca4232cbae322fdc7b68ed21fcd679
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Feb 15 17:07:13 2022 +0100

    -correctly implement CS idempotency check on withdraw
---
 src/exchange/taler-exchange-httpd_recoup.c   | 24 +++----
 src/exchange/taler-exchange-httpd_withdraw.c | 25 ++++++--
 src/exchangedb/exchange-0001.sql             | 42 ++++++++++--
 src/exchangedb/plugin_exchangedb_postgres.c  | 96 +++++++++++++++-------------
 src/exchangedb/test_exchangedb.c             | 27 +++++---
 src/include/taler_crypto_lib.h               | 32 ++++++++++
 src/include/taler_exchangedb_plugin.h        | 29 +++++----
 src/util/denom.c                             | 37 +++++++++++
 8 files changed, 220 insertions(+), 92 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_recoup.c 
b/src/exchange/taler-exchange-httpd_recoup.c
index c635769c..ea319d11 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2017-2021 Taler Systems SA
+  Copyright (C) 2017-2022 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -40,9 +40,9 @@
 struct RecoupContext
 {
   /**
-   * Hash of the blinded coin.
+   * Hash identifying the withdraw request.
    */
-  struct TALER_BlindedCoinHash h_blind;
+  struct TALER_WithdrawIdentificationHash wih;
 
   /**
    * Set by #recoup_transaction() to the reserve that will
@@ -273,9 +273,9 @@ verify_and_execute_recoup (
       blinded_planchet.details.cs_blinded_planchet.nonce
         = *nonce;
     if (GNUNET_OK !=
-        TALER_coin_ev_hash (&blinded_planchet,
-                            &coin->denom_pub_hash,
-                            &pc.h_blind))
+        TALER_withdraw_request_hash (&blinded_planchet,
+                                     &coin->denom_pub_hash,
+                                     &pc.wih))
     {
       GNUNET_break (0);
       return TALER_MHD_reply_with_error (connection,
@@ -308,10 +308,10 @@ verify_and_execute_recoup (
   {
     enum GNUNET_DB_QueryStatus qs;
 
-    qs = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
-                                             &pc.h_blind,
-                                             &pc.reserve_pub,
-                                             &pc.reserve_out_serial_id);
+    qs = TEH_plugin->get_reserve_by_wih (TEH_plugin->cls,
+                                         &pc.wih,
+                                         &pc.reserve_pub,
+                                         &pc.reserve_out_serial_id);
     if (0 > qs)
     {
       GNUNET_break (0);
@@ -319,13 +319,13 @@ verify_and_execute_recoup (
         connection,
         MHD_HTTP_INTERNAL_SERVER_ERROR,
         TALER_EC_GENERIC_DB_FETCH_FAILED,
-        "get_reserve_by_h_blind");
+        "get_reserve_by_wih");
     }
     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Recoup requested for unknown envelope %s\n",
-                  GNUNET_h2s (&pc.h_blind.hash));
+                  GNUNET_h2s (&pc.wih.hash));
       return TALER_MHD_reply_with_error (
         connection,
         MHD_HTTP_NOT_FOUND,
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
index 3799187c..a3ac1de3 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -91,6 +91,11 @@ reply_withdraw_insufficient_funds (
 struct WithdrawContext
 {
 
+  /**
+   * Hash that uniquely identifies the withdraw request.
+   */
+  struct TALER_WithdrawIdentificationHash wih;
+
   /**
    * Hash of the (blinded) message to be signed by the Exchange.
    */
@@ -155,6 +160,7 @@ withdraw_transaction (void *cls,
 
   now = GNUNET_TIME_timestamp_get ();
   qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
+                                &wc->wih,
                                 &wc->collectable,
                                 now,
                                 &found,
@@ -294,7 +300,7 @@ check_request_idempotent (struct TEH_RequestContext *rc,
   enum GNUNET_DB_QueryStatus qs;
 
   qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
-                                      &wc->collectable.h_coin_envelope,
+                                      &wc->wih,
                                       &wc->collectable);
   if (0 > qs)
   {
@@ -496,7 +502,18 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
                                        NULL);
   }
 
-  // TODO: if CS: check nonce for reuse
+  if (GNUNET_OK !=
+      TALER_withdraw_request_hash (&wc.blinded_planchet,
+                                   &wc.collectable.denom_pub_hash,
+                                   &wc.wih))
+  {
+    GNUNET_break (0);
+    GNUNET_JSON_parse_free (spec);
+    return TALER_MHD_reply_with_error (rc->connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                                       NULL);
+  }
 
   /* Sign before transaction! */
   ec = TEH_keys_denomination_sign (
@@ -535,10 +552,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
   /* Clean up and send back final response */
   GNUNET_JSON_parse_free (spec);
 
-  // FIXME: in CS-case, we MUST re-transmit any _existing_ signature
-  // (if database had a record matching the nonce)
-  // instead of sending a 'fresh' one back (as c0/c1 may differ in
-  // a client attack!
   {
     MHD_RESULT ret;
 
diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql
index 66856f60..1111f381 100644
--- a/src/exchangedb/exchange-0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -196,7 +196,8 @@ CREATE INDEX IF NOT EXISTS 
reserves_close_by_reserve_pub_index
 
 CREATE TABLE IF NOT EXISTS reserves_out
   (reserve_out_serial_id BIGSERIAL -- UNIQUE
-  ,h_blind_ev BYTEA PRIMARY KEY CHECK (LENGTH(h_blind_ev)=64)
+  ,wih BYTEA PRIMARY KEY CHECK (LENGTH(wih)=64)
+  ,h_blind_ev BYTEA CHECK (LENGTH(h_blind_ev)=64) -- UNIQUE
   ,denominations_serial INT8 NOT NULL REFERENCES denominations 
(denominations_serial)
   ,denom_sig BYTEA NOT NULL
   ,reserve_uuid INT8 NOT NULL -- REFERENCES reserves (reserve_uuid) ON DELETE 
CASCADE
@@ -205,9 +206,11 @@ CREATE TABLE IF NOT EXISTS reserves_out
   ,amount_with_fee_val INT8 NOT NULL
   ,amount_with_fee_frac INT4 NOT NULL
   )
-  PARTITION BY HASH (h_blind_ev);
+  PARTITION BY HASH (wih);
 COMMENT ON TABLE reserves_out
   IS 'Withdraw operations performed on reserves.';
+COMMENT ON COLUMN reserves_out.wih
+  IS 'Hash that uniquely identifies the withdraw request. Used to detect 
request replays (crucial for CS) and to check the withdraw existed during 
recoup.';
 COMMENT ON COLUMN reserves_out.h_blind_ev
   IS 'Hash of the blinded coin, used as primary key here so that broken 
clients that use a non-random coin or blinding factor fail to withdraw 
(otherwise they would fail on deposit when the coin is not unique there).';
 COMMENT ON COLUMN reserves_out.denominations_serial
@@ -637,7 +640,7 @@ COMMENT ON TABLE recoup
 COMMENT ON COLUMN recoup.known_coin_id
   IS 'Coin that is being debited in the recoup. Do not CASCADE ON DROP on the 
coin_pub, as we may keep the coin alive!';
 COMMENT ON COLUMN recoup.reserve_out_serial_id
-  IS 'Identifies the h_blind_ev of the recouped coin and provides the link to 
the credited reserve.';
+  IS 'Identifies the wih of the recouped coin and provides the link to the 
credited reserve.';
 COMMENT ON COLUMN recoup.coin_sig
   IS 'Signature by the coin affirming the recoup, of type 
TALER_SIGNATURE_WALLET_COIN_RECOUP';
 COMMENT ON COLUMN recoup.coin_blind
@@ -812,6 +815,7 @@ CREATE INDEX IF NOT EXISTS 
revolving_work_shards_by_job_name_active_last_attempt
 
 
 CREATE OR REPLACE FUNCTION exchange_do_withdraw(
+  IN in_wih BYTEA,
   IN amount_val INT8,
   IN amount_frac INT4,
   IN h_denom_pub BYTEA,
@@ -825,7 +829,8 @@ CREATE OR REPLACE FUNCTION exchange_do_withdraw(
   OUT balance_ok BOOLEAN,
   OUT kycok BOOLEAN,
   OUT account_uuid INT8,
-  OUT ruuid INT8)
+  OUT ruuid INT8,
+  OUT out_denom_sig BYTEA)
 LANGUAGE plpgsql
 AS $$
 DECLARE
@@ -838,7 +843,7 @@ DECLARE
   reserve_frac INT4;
 BEGIN
 -- Shards: reserves by reserve_pub (SELECT)
---         reserves_out (INSERT, with CONFLICT detection) by h_blind_ev
+--         reserves_out (INSERT, with CONFLICT detection) by wih
 --         reserves by reserve_pub (UPDATE)
 --         reserves_in by reserve_pub (SELECT)
 --         wire_targets by wire_target_serial_id
@@ -887,6 +892,7 @@ END IF;
 -- the query successful due to idempotency.
 INSERT INTO reserves_out
   (h_blind_ev
+  ,wih
   ,denominations_serial
   ,denom_sig
   ,reserve_uuid
@@ -896,6 +902,7 @@ INSERT INTO reserves_out
   ,amount_with_fee_frac)
 VALUES
   (h_coin_envelope
+  ,in_wih
   ,denom_serial
   ,denom_sig
   ,ruuid
@@ -908,6 +915,25 @@ ON CONFLICT DO NOTHING;
 IF NOT FOUND
 THEN
   -- idempotent query, all constraints must be satisfied
+
+  SELECT
+     denom_sig
+  INTO
+     out_denom_sig
+  FROM reserves_in
+    WHERE wih=in_wih
+    LIMIT 1; -- limit 1 should not be required (without p2p transfers)
+
+  IF NOT FOUND
+  THEN
+    reserve_found=FALSE;
+    balance_ok=FALSE;
+    kycok=FALSE;
+    account_uuid=0;
+    ruuid=0;
+    ASSERT false, 'internal logic error';
+  END IF;
+
   reserve_found=TRUE;
   balance_ok=TRUE;
   kycok=TRUE;
@@ -967,9 +993,13 @@ SELECT
  WHERE reserve_pub=rpub
  LIMIT 1; -- limit 1 should not be required (without p2p transfers)
 
+-- Return denomination signature as result that
+-- was given as the argument.
+out_denom_sig=denom_sig;
+
 END $$;
 
-COMMENT ON FUNCTION exchange_do_withdraw(INT8, INT4, BYTEA, BYTEA, BYTEA, 
BYTEA, BYTEA, INT8, INT8)
+COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, 
BYTEA, BYTEA, BYTEA, INT8, INT8)
   IS 'Checks whether the reserve has sufficient balance for a withdraw 
operation (or the request is repeated and was previously approved) and if so 
updates the database with the result';
 
 
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index ce184f48..98724fa0 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -560,13 +560,6 @@ prepare_statements (struct PostgresClosure *pg)
       "   ON (wire_source_serial_id = wire_target_serial_id)"
       " WHERE reserve_pub=$1;",
       1),
-    /* Lock withdraw table; NOTE: we may want to eventually shard the
-       deposit table to avoid this lock being the main point of
-       contention limiting transaction performance. */
-    GNUNET_PQ_make_prepare (
-      "lock_withdraw",
-      "LOCK TABLE reserves_out;",
-      0),
     /* Used in #postgres_do_withdraw() to store
        the signature of a blinded coin with the blinded coin's
        details before returning it during /reserve/withdraw. We store
@@ -582,9 +575,10 @@ prepare_statements (struct PostgresClosure *pg)
       ",kycok AS kyc_ok"
       ",account_uuid AS payment_target_uuid"
       ",ruuid"
+      ",out_denom_sig"
       " FROM exchange_do_withdraw"
-      " ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
-      9),
+      " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
+      10),
     /* Used in #postgres_do_withdraw_limit_check() to check
        if the withdrawals remain below the limit under which
        KYC is not required. */
@@ -659,6 +653,7 @@ prepare_statements (struct PostgresClosure *pg)
       ",reserve_sig"
       ",reserves.reserve_pub"
       ",execution_date"
+      ",h_blind_ev"
       ",amount_with_fee_val"
       ",amount_with_fee_frac"
       ",denom.fee_withdraw_val"
@@ -668,7 +663,7 @@ prepare_statements (struct PostgresClosure *pg)
       "      USING (reserve_uuid)"
       "    JOIN denominations denom"
       "      USING (denominations_serial)"
-      " WHERE h_blind_ev=$1;",
+      " WHERE wih=$1;",
       1),
     /* Used during #postgres_get_reserve_history() to
        obtain all of the /reserve/withdraw operations that
@@ -1671,16 +1666,16 @@ prepare_statements (struct PostgresClosure *pg)
       "      ON (denoms.denominations_serial = coins.denominations_serial)"
       " WHERE coins.coin_pub=$1;",
       1),
-    /* Used in #postgres_get_reserve_by_h_blind() */
+    /* Used in #postgres_get_reserve_by_wih() */
     GNUNET_PQ_make_prepare (
-      "reserve_by_h_blind",
+      "reserve_by_wih",
       "SELECT"
       " reserves.reserve_pub"
       ",reserve_out_serial_id"
       " FROM reserves_out"
       " JOIN reserves"
       "   USING (reserve_uuid)"
-      " WHERE h_blind_ev=$1"
+      " WHERE wih=$1"
       " LIMIT 1;",
       1),
     /* Used in #postgres_get_old_coin_by_h_blind() */
@@ -4304,8 +4299,7 @@ postgres_reserves_in_insert (void *cls,
  * key of the hash of the blinded message.
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param h_blind hash of the blinded coin to be signed (will match
- *                `h_coin_envelope` in the @a collectable to be returned)
+ * @param wih hash that uniquely identifies the withdraw operation
  * @param collectable corresponding collectable coin (blind signature)
  *                    if a coin is found
  * @return statement execution status
@@ -4313,12 +4307,12 @@ postgres_reserves_in_insert (void *cls,
 static enum GNUNET_DB_QueryStatus
 postgres_get_withdraw_info (
   void *cls,
-  const struct TALER_BlindedCoinHash *h_blind,
+  const struct TALER_WithdrawIdentificationHash *wih,
   struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_blind),
+    GNUNET_PQ_query_param_auto_from_type (wih),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
@@ -4330,24 +4324,15 @@ postgres_get_withdraw_info (
                                           &collectable->reserve_sig),
     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
                                           &collectable->reserve_pub),
+    GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
+                                          &collectable->h_coin_envelope),
     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
                                  &collectable->amount_with_fee),
     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
                                  &collectable->withdraw_fee),
     GNUNET_PQ_result_spec_end
   };
-#if EXPLICIT_LOCKS
-  struct GNUNET_PQ_QueryParam no_params[] = {
-    GNUNET_PQ_query_param_end
-  };
-  enum GNUNET_DB_QueryStatus qs;
 
-  if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                                    "lock_withdraw",
-                                                    no_params)))
-    return qs;
-#endif
-  collectable->h_coin_envelope = *h_blind;
   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
                                                    "get_withdraw_info",
                                                    params,
@@ -4360,8 +4345,8 @@ postgres_get_withdraw_info (
  * and possibly persisting the withdrawal details.
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param collectable corresponding collectable coin (blind signature)
- *                    if a coin is found
+ * @param wih hash that uniquely identifies the withdraw operation
+ * @param[in,out] collectable corresponding collectable coin (blind signature) 
if a coin is found; possibly updated if a (different) signature exists already
  * @param now current time (rounded)
  * @param[out] found set to true if the reserve was found
  * @param[out] balance_ok set to true if the balance was sufficient
@@ -4372,7 +4357,8 @@ postgres_get_withdraw_info (
 static enum GNUNET_DB_QueryStatus
 postgres_do_withdraw (
   void *cls,
-  const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
+  const struct TALER_WithdrawIdentificationHash *wih,
+  struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
   struct GNUNET_TIME_Timestamp now,
   bool *found,
   bool *balance_ok,
@@ -4382,6 +4368,7 @@ postgres_do_withdraw (
   struct PostgresClosure *pg = cls;
   struct GNUNET_TIME_Timestamp gc;
   struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (wih),
     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
     GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
@@ -4392,6 +4379,9 @@ postgres_do_withdraw (
     GNUNET_PQ_query_param_timestamp (&gc),
     GNUNET_PQ_query_param_end
   };
+  enum GNUNET_DB_QueryStatus qs;
+  bool no_out_sig;
+  struct TALER_BlindedDenominationSignature out_sig;
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_bool ("reserve_found",
                                 found),
@@ -4403,18 +4393,33 @@ postgres_do_withdraw (
                                   &kyc->payment_target_uuid),
     GNUNET_PQ_result_spec_uint64 ("ruuid",
                                   ruuid),
+    GNUNET_PQ_result_spec_allow_null (
+      TALER_PQ_result_spec_blinded_denom_sig ("out_denom_sig",
+                                              &out_sig),
+      &no_out_sig),
     GNUNET_PQ_result_spec_end
   };
 
+#if 0
+  memset (&out_sig,
+          0,
+          sizeof (out_sig));
+#endif
   gc = GNUNET_TIME_absolute_to_timestamp (
     GNUNET_TIME_absolute_add (now.abs_time,
                               pg->legal_reserve_expiration_time));
   kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "call_withdraw",
-                                                   params,
-                                                   rs);
-
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "call_withdraw",
+                                                 params,
+                                                 rs);
+  if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
+       (! no_out_sig) )
+  {
+    TALER_blinded_denom_sig_free (&collectable->sig);
+    collectable->sig = out_sig;
+  }
+  return qs;
 }
 
 
@@ -9373,20 +9378,21 @@ postgres_select_reserve_closed_above_serial_id (
  * from given the hash of the blinded coin.
  *
  * @param cls closure
- * @param h_blind_ev hash of the blinded coin
+ * @param wih hash that uniquely identifies the withdraw request
  * @param[out] reserve_pub set to information about the reserve (on success 
only)
  * @param[out] reserve_out_serial_id set to row of the @a h_blind_ev in 
reserves_out
  * @return transaction status code
  */
 static enum GNUNET_DB_QueryStatus
-postgres_get_reserve_by_h_blind (void *cls,
-                                 const struct TALER_BlindedCoinHash 
*h_blind_ev,
-                                 struct TALER_ReservePublicKeyP *reserve_pub,
-                                 uint64_t *reserve_out_serial_id)
+postgres_get_reserve_by_wih (
+  void *cls,
+  const struct TALER_WithdrawIdentificationHash *wih,
+  struct TALER_ReservePublicKeyP *reserve_pub,
+  uint64_t *reserve_out_serial_id)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
+    GNUNET_PQ_query_param_auto_from_type (wih),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
@@ -9398,7 +9404,7 @@ postgres_get_reserve_by_h_blind (void *cls,
   };
 
   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "reserve_by_h_blind",
+                                                   "reserve_by_wih",
                                                    params,
                                                    rs);
 }
@@ -11663,8 +11669,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
     = &postgres_select_recoup_refresh_above_serial_id;
   plugin->select_reserve_closed_above_serial_id
     = &postgres_select_reserve_closed_above_serial_id;
-  plugin->get_reserve_by_h_blind
-    = &postgres_get_reserve_by_h_blind;
+  plugin->get_reserve_by_wih
+    = &postgres_get_reserve_by_wih;
   plugin->get_old_coin_by_h_blind
     = &postgres_get_old_coin_by_h_blind;
   plugin->insert_denomination_revocation
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 9561df12..0622e069 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1346,6 +1346,7 @@ run (void *cls)
   struct GNUNET_TIME_Timestamp now;
   struct TALER_WireSaltP salt;
   struct TALER_CoinPubHash c_hash;
+  struct TALER_WithdrawIdentificationHash wih;
   uint64_t known_coin_id;
   uint64_t rrc_serial;
   struct TALER_EXCHANGEDB_Refresh refresh;
@@ -1383,7 +1384,7 @@ run (void *cls)
       plugin->create_tables (plugin->cls))
   {
     result = 77;
-    goto drop;
+    goto cleanup;
   }
   plugin->preflight (plugin->cls);
   FAILIF (GNUNET_OK !=
@@ -1499,9 +1500,14 @@ run (void *cls)
                                          &cbc.denom_pub_hash,
                                          &cbc.h_coin_envelope));
       GNUNET_assert (GNUNET_OK ==
-                     TALER_denom_sign_blinded (&cbc.sig,
-                                               &dkp->priv,
-                                               &pd.blinded_planchet));
+                     TALER_withdraw_request_hash (&pd.blinded_planchet,
+                                                  &cbc.denom_pub_hash,
+                                                  &wih));      GNUNET_assert (
+        GNUNET_OK ==
+        TALER_denom_sign_blinded (
+          &cbc.sig,
+          &dkp->priv,
+          &pd.blinded_planchet));
       TALER_blinded_planchet_free (&pd.blinded_planchet);
     }
   }
@@ -1511,6 +1517,7 @@ run (void *cls)
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_set_zero (CURRENCY,
                                         &cbc.withdraw_fee));
+
   {
     bool found;
     bool balance_ok;
@@ -1519,6 +1526,7 @@ run (void *cls)
 
     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
             plugin->do_withdraw (plugin->cls,
+                                 &wih,
                                  &cbc,
                                  now,
                                  &found,
@@ -1540,16 +1548,16 @@ run (void *cls)
                          value.fraction,
                          value.currency));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_reserve_by_h_blind (plugin->cls,
-                                          &cbc.h_coin_envelope,
-                                          &reserve_pub3,
-                                          &reserve_out_serial_id));
+          plugin->get_reserve_by_wih (plugin->cls,
+                                      &wih,
+                                      &reserve_pub3,
+                                      &reserve_out_serial_id));
   FAILIF (0 != GNUNET_memcmp (&reserve_pub,
                               &reserve_pub3));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_withdraw_info (plugin->cls,
-                                     &cbc.h_coin_envelope,
+                                     &wih,
                                      &cbc2));
   FAILIF (0 != GNUNET_memcmp (&cbc2.reserve_sig,
                               &cbc.reserve_sig));
@@ -2400,6 +2408,7 @@ drop:
   rh = NULL;
   GNUNET_break (GNUNET_OK ==
                 plugin->drop_tables (plugin->cls));
+cleanup:
   if (NULL != dkp)
     destroy_denom_key_pair (dkp);
   if (NULL != revealed_coins)
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index f007d67a..ab5202ba 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -571,6 +571,22 @@ struct TALER_BlindedCoinHash
 };
 
 
+/**
+ * Hash used to uniquely represent a withdraw process so as to perform
+ * idempotency checks (and prevent clients from harmfully replaying withdraw
+ * operations with problematic variations on the inputs).  In the CS case,
+ * this is a hash over the DK and nonce, while in the RSA case, it is simply a
+ * hash over the DK and the blinded coin.
+ */
+struct TALER_WithdrawIdentificationHash
+{
+  /**
+   * Actual hash value.
+   */
+  struct GNUNET_HashCode hash;
+};
+
+
 /**
  * Hash used to represent the hash of the public
  * key of a coin (without blinding).
@@ -1308,6 +1324,22 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet 
*blinded_planchet,
                     struct TALER_BlindedCoinHash *bch);
 
 
+/**
+ * Compute the hash to uniquely identify a withdraw
+ * request.
+ *
+ * @param blinded_planchet blinded planchet
+ * @param denom_hash hash of the denomination publick key
+ * @param[out] wih where to write the hash
+ * @return #GNUNET_OK when successful, #GNUNET_SYSERR if an internal error 
occured
+ */
+enum GNUNET_GenericReturnValue
+TALER_withdraw_request_hash (
+  const struct TALER_BlindedPlanchet *blinded_planchet,
+  const struct TALER_DenominationHash *denom_hash,
+  struct TALER_WithdrawIdentificationHash *wih);
+
+
 /**
  * Compute the hash of a coin.
  *
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index eea170c1..ec647e9c 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -2476,20 +2476,19 @@ struct TALER_EXCHANGEDB_Plugin
 
 
   /**
-   * Locate the response for a withdraw request under the
-   * key of the hash of the blinded message.  Used to ensure
-   * idempotency of the request.
+   * Locate the response for a withdraw request under a hash that uniquely
+   * identifies the withdraw operation.  Used to ensure idempotency of the
+   * request.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
-   * @param h_blind hash of the blinded coin to be signed (will match
-   *                `h_coin_envelope` in the @a collectable to be returned)
-   * @param collectable corresponding collectable coin (blind signature)
+   * @param wih hash that uniquely identifies the withdraw operation
+   * @param[out] collectable corresponding collectable coin (blind signature)
    *                    if a coin is found
    * @return statement execution status
    */
   enum GNUNET_DB_QueryStatus
   (*get_withdraw_info)(void *cls,
-                       const struct TALER_BlindedCoinHash *h_blind,
+                       const struct TALER_WithdrawIdentificationHash *wih,
                        struct TALER_EXCHANGEDB_CollectableBlindcoin 
*collectable);
 
 
@@ -2498,7 +2497,8 @@ struct TALER_EXCHANGEDB_Plugin
    * and possibly persisting the withdrawal details.
    *
    * @param cls the `struct PostgresClosure` with the plugin-specific state
-   * @param collectable corresponding collectable coin (blind signature)
+   * @param wih hash that uniquely identifies the withdraw operation
+   * @param[in,out] collectable corresponding collectable coin (blind 
signature)
    *                    if a coin is found
    * @param now current time (rounded)
    * @param[out] found set to true if the reserve was found
@@ -2510,7 +2510,8 @@ struct TALER_EXCHANGEDB_Plugin
   enum GNUNET_DB_QueryStatus
   (*do_withdraw)(
     void *cls,
-    const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
+    const struct TALER_WithdrawIdentificationHash *wih,
+    struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
     struct GNUNET_TIME_Timestamp now,
     bool *found,
     bool *balance_ok,
@@ -3517,16 +3518,16 @@ struct TALER_EXCHANGEDB_Plugin
    * from given the hash of the blinded coin.
    *
    * @param cls closure
-   * @param h_blind_ev hash of the blinded coin
+   * @param wih hash identifying the withdraw operation
    * @param[out] reserve_pub set to information about the reserve (on success 
only)
    * @param[out] reserve_out_serial_id set to row of the @a h_blind_ev in 
reserves_out
    * @return transaction status code
    */
   enum GNUNET_DB_QueryStatus
-  (*get_reserve_by_h_blind)(void *cls,
-                            const struct TALER_BlindedCoinHash *h_blind_ev,
-                            struct TALER_ReservePublicKeyP *reserve_pub,
-                            uint64_t *reserve_out_serial_id);
+  (*get_reserve_by_wih)(void *cls,
+                        const struct TALER_WithdrawIdentificationHash *wih,
+                        struct TALER_ReservePublicKeyP *reserve_pub,
+                        uint64_t *reserve_out_serial_id);
 
 
   /**
diff --git a/src/util/denom.c b/src/util/denom.c
index 2e7b1264..783e9a36 100644
--- a/src/util/denom.c
+++ b/src/util/denom.c
@@ -828,4 +828,41 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet 
*blinded_planchet,
 }
 
 
+enum GNUNET_GenericReturnValue
+TALER_withdraw_request_hash (
+  const struct TALER_BlindedPlanchet *blinded_planchet,
+  const struct TALER_DenominationHash *denom_hash,
+  struct TALER_WithdrawIdentificationHash *wih)
+{
+  struct GNUNET_HashContext *hash_context;
+
+  hash_context = GNUNET_CRYPTO_hash_context_start ();
+  GNUNET_CRYPTO_hash_context_read (hash_context,
+                                   denom_hash,
+                                   sizeof(*denom_hash));
+  switch (blinded_planchet->cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    GNUNET_CRYPTO_hash_context_read (
+      hash_context,
+      blinded_planchet->details.rsa_blinded_planchet.blinded_msg,
+      blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size);
+    break;
+  case TALER_DENOMINATION_CS:
+    GNUNET_CRYPTO_hash_context_read (
+      hash_context,
+      &blinded_planchet->details.cs_blinded_planchet.nonce,
+      sizeof (struct TALER_CsNonce));
+    break;
+  default:
+    GNUNET_break (0);
+    GNUNET_CRYPTO_hash_context_abort (hash_context);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CRYPTO_hash_context_finish (hash_context,
+                                     &wih->hash);
+  return GNUNET_OK;
+}
+
+
 /* end of denom.c */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]