gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: test coin_priv re-use with depos


From: gnunet
Subject: [taler-exchange] branch master updated: test coin_priv re-use with deposit and refresh, update handling of the error code client-side
Date: Fri, 10 Jul 2020 23:09:48 +0200

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 7085cfef test coin_priv re-use with deposit and refresh, update 
handling of the error code client-side
7085cfef is described below

commit 7085cfef70889a11508cdac4cd887b9959f59218
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Jul 10 23:09:46 2020 +0200

    test coin_priv re-use with deposit and refresh, update handling of the 
error code client-side
---
 src/include/taler_testing_lib.h        |  24 +++++++
 src/lib/exchange_api_melt.c            |  83 ++++++++++++++++++++++--
 src/testing/test_exchange_api.c        |  43 +++++++++---
 src/testing/testing_api_cmd_withdraw.c | 115 ++++++++++++++++++++++++++++++++-
 4 files changed, 249 insertions(+), 16 deletions(-)

diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index c3278b55..332b429f 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -1295,6 +1295,30 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
                                    unsigned int expected_response_code);
 
 
+/**
+ * Create a withdraw command, letting the caller specify
+ * the desired amount as string and also re-using an existing
+ * coin private key in the process (violating the specification,
+ * which will result in an error when spending the coin!).
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw 
from
+ * @param amount how much we withdraw.
+ * @param coin_ref reference to (withdraw/reveal) command of a coin
+ *        from which we should re-use the private key
+ * @param expected_response_code which HTTP response code
+ *        we expect from the exchange.
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_amount_reuse_key (
+  const char *label,
+  const char *reserve_reference,
+  const char *amount,
+  const char *coin_ref,
+  unsigned int expected_response_code);
+
+
 /**
  * Create withdraw command, letting the caller specify the
  * amount by a denomination key.
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
index 2c1e85d7..989c6501 100644
--- a/src/lib/exchange_api_melt.c
+++ b/src/lib/exchange_api_melt.c
@@ -75,6 +75,11 @@ struct TALER_EXCHANGE_MeltHandle
    */
   struct MeltData *md;
 
+  /**
+   * Public key of the coin being melted.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
   /**
    * @brief Public information about the coin's denomination key
    */
@@ -153,6 +158,48 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle 
*mh,
 }
 
 
+/**
+ * Verify that the signatures on the "409 CONFLICT" response from the
+ * exchange demonstrating customer denomination key differences
+ * resulting from coin private key reuse are valid.
+ *
+ * @param mh melt handle
+ * @param json json reply with the signature(s) and transaction history
+ * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_melt_signature_denom_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
+                                      const json_t *json)
+
+{
+  json_t *history;
+  struct TALER_Amount total;
+  struct GNUNET_HashCode h_denom_pub;
+
+  memset (&h_denom_pub,
+          0,
+          sizeof (h_denom_pub));
+  history = json_object_get (json,
+                             "history");
+  if (GNUNET_OK !=
+      TALER_EXCHANGE_verify_coin_history (&mh->dki,
+                                          mh->dki.value.currency,
+                                          &mh->coin_pub,
+                                          history,
+                                          &h_denom_pub,
+                                          &total))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (0 != GNUNET_memcmp (&mh->dki.h_key,
+                          &h_denom_pub))
+    return GNUNET_OK; /* indeed, proof with different denomination key 
provided */
+  /* invalid proof provided */
+  return GNUNET_SYSERR;
+}
+
+
 /**
  * Verify that the signatures on the "409 CONFLICT" response from the
  * exchange demonstrating customer double-spending are valid.
@@ -162,8 +209,8 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle 
*mh,
  * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
  */
 static int
-verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
-                                const json_t *json)
+verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
+                                      const json_t *json)
 {
   json_t *history;
   struct TALER_Amount original_value;
@@ -329,15 +376,38 @@ handle_melt_finished (void *cls,
     hr.hint = TALER_JSON_get_error_hint (j);
     break;
   case MHD_HTTP_CONFLICT:
-    /* Double spending; check signatures on transaction history */
-    if (GNUNET_OK !=
-        verify_melt_signature_conflict (mh,
-                                        j))
+    hr.ec = TALER_JSON_get_error_code (j);
+    switch (hr.ec)
     {
+    case TALER_EC_MELT_INSUFFICIENT_FUNDS:
+      /* Double spending; check signatures on transaction history */
+      if (GNUNET_OK !=
+          verify_melt_signature_spend_conflict (mh,
+                                                j))
+      {
+        GNUNET_break_op (0);
+        hr.http_status = 0;
+        hr.ec = TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
+        hr.hint = TALER_JSON_get_error_hint (j);
+      }
+      break;
+    case TALER_EC_COIN_CONFLICTING_DENOMINATION_KEY:
+      if (GNUNET_OK !=
+          verify_melt_signature_denom_conflict (mh,
+                                                j))
+      {
+        GNUNET_break_op (0);
+        hr.http_status = 0;
+        hr.ec = TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
+        hr.hint = TALER_JSON_get_error_hint (j);
+      }
+      break;
+    default:
       GNUNET_break_op (0);
       hr.http_status = 0;
       hr.ec = TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
       hr.hint = TALER_JSON_get_error_hint (j);
+      break;
     }
     break;
   case MHD_HTTP_FORBIDDEN:
@@ -485,6 +555,7 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
   /* and now we can at last begin the actual request handling */
   mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
   mh->exchange = exchange;
+  mh->coin_pub = melt.coin_pub;
   mh->dki = *dki;
   mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
                                          not copy the pointer */
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index 410c1a49..75e3d480 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -136,12 +136,12 @@ run (void *cls,
      * Do another transfer to the same reserve
      */
     TALER_TESTING_cmd_admin_add_incoming_with_ref ("create-reserve-1.2",
-                                                   "EUR:1",
+                                                   "EUR:2.01",
                                                    &bc.exchange_auth,
                                                    bc.user42_payto,
                                                    "create-reserve-1"),
     TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1.2",
-                                                 "EUR:1",
+                                                 "EUR:2.01",
                                                  bc.user42_payto,
                                                  bc.exchange_payto,
                                                  "create-reserve-1.2"),
@@ -153,6 +153,15 @@ run (void *cls,
                                        "create-reserve-1",
                                        "EUR:5",
                                        MHD_HTTP_OK),
+    /**
+     * Withdraw EUR:1 using the SAME private coin key as for the previous coin
+     * (in violation of the specification, to be detected on spending!).
+     */
+    TALER_TESTING_cmd_withdraw_amount_reuse_key ("withdraw-coin-1x",
+                                                 "create-reserve-1",
+                                                 "EUR:1",
+                                                 "withdraw-coin-1",
+                                                 MHD_HTTP_OK),
     /**
      * Check the reserve is depleted.
      */
@@ -160,6 +169,13 @@ run (void *cls,
                               "create-reserve-1",
                               "EUR:0",
                               MHD_HTTP_OK),
+    /*
+     * Try to overdraw.
+     */
+    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
+                                       "create-reserve-1",
+                                       "EUR:5",
+                                       MHD_HTTP_CONFLICT),
     TALER_TESTING_cmd_end ()
   };
 
@@ -178,13 +194,14 @@ run (void *cls,
     TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay",
                                       "deposit-simple",
                                       MHD_HTTP_OK),
-    /*
-     * Try to overdraw.
-     */
-    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
-                                       "create-reserve-1",
-                                       "EUR:5",
-                                       MHD_HTTP_CONFLICT),
+    TALER_TESTING_cmd_deposit ("deposit-reused-coin-key-failure",
+                               "withdraw-coin-1x",
+                               0,
+                               bc.user42_payto,
+                               "{\"items\":[{\"name\":\"ice 
cream\",\"value\":1}]}",
+                               GNUNET_TIME_UNIT_ZERO,
+                               "EUR:1",
+                               MHD_HTTP_CONFLICT),
     /**
      * Try to double spend using different wire details.
      */
@@ -225,6 +242,14 @@ run (void *cls,
   };
 
   struct TALER_TESTING_Command refresh[] = {
+    /**
+     * Try to melt the coin that shared the private key with another
+     * coin (should fail). */
+    TALER_TESTING_cmd_melt ("refresh-melt-reused-coin-key-failure",
+                            "withdraw-coin-1x",
+                            MHD_HTTP_CONFLICT,
+                            NULL),
+
     /* Fill reserve with EUR:5, 1ct is for fees. */
     CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
                               "EUR:5.01"),
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index 5db97cbf..5b2ad26e 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -59,6 +59,12 @@ struct WithdrawState
    */
   const char *reserve_reference;
 
+  /**
+   * Reference to a withdraw or reveal operation from which we should
+   * re-use the private coin key, or NULL for regular withdrawal.
+   */
+  const char *reuse_coin_key_ref;
+
   /**
    * String describing the denomination value we should withdraw.
    * A corresponding denomination key must exist in the exchange's
@@ -274,6 +280,50 @@ reserve_withdraw_cb (void *cls,
 }
 
 
+/**
+ * Parser reference to a coin.
+ *
+ * @param coin_reference of format $LABEL['#' $INDEX]?
+ * @param[out] cref where we return a copy of $LABEL
+ * @param[out] idx where we set $INDEX
+ * @return #GNUNET_SYSERR if $INDEX is present but not numeric
+ */
+static int
+parse_coin_reference (const char *coin_reference,
+                      char **cref,
+                      unsigned int *idx)
+{
+  const char *index;
+
+  /* We allow command references of the form "$LABEL#$INDEX" or
+     just "$LABEL", which implies the index is 0. Figure out
+     which one it is. */
+  index = strchr (coin_reference, '#');
+  if (NULL == index)
+  {
+    *idx = 0;
+    *cref = GNUNET_strdup (coin_reference);
+    return GNUNET_OK;
+  }
+  *cref = GNUNET_strndup (coin_reference,
+                          index - coin_reference);
+  if (1 != sscanf (index + 1,
+                   "%u",
+                   idx))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Numeric index (not `%s') required after `#' in command 
reference of command in %s:%u\n",
+                index,
+                __FILE__,
+                __LINE__);
+    GNUNET_free (*cref);
+    *cref = NULL;
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * Run the command.
  */
@@ -307,7 +357,32 @@ withdraw_run (void *cls,
     TALER_TESTING_interpreter_fail (is);
     return;
   }
-  TALER_planchet_setup_random (&ws->ps);
+  if (NULL == ws->reuse_coin_key_ref)
+  {
+    TALER_planchet_setup_random (&ws->ps);
+  }
+  else
+  {
+    const struct TALER_CoinSpendPrivateKeyP *coin_priv;
+    const struct TALER_TESTING_Command *cref;
+    char *cstr;
+    unsigned int index;
+
+    GNUNET_assert (GNUNET_OK ==
+                   parse_coin_reference (ws->reuse_coin_key_ref,
+                                         &cstr,
+                                         &index));
+    cref = TALER_TESTING_interpreter_lookup_command (is,
+                                                     cstr);
+    GNUNET_assert (NULL != cref);
+    GNUNET_free (cstr);
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_TESTING_get_trait_coin_priv (cref,
+                                                      index,
+                                                      &coin_priv));
+    TALER_planchet_setup_random (&ws->ps);
+    ws->ps.coin_priv = *coin_priv;
+  }
   ws->is = is;
   if (NULL == ws->pk)
   {
@@ -526,6 +601,44 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
 }
 
 
+/**
+ * Create a withdraw command, letting the caller specify
+ * the desired amount as string and also re-using an existing
+ * coin private key in the process (violating the specification,
+ * which will result in an error when spending the coin!).
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw 
from
+ * @param amount how much we withdraw.
+ * @param coin_ref reference to (withdraw/reveal) command of a coin
+ *        from which we should re-use the private key
+ * @param expected_response_code which HTTP response code
+ *        we expect from the exchange.
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_amount_reuse_key (
+  const char *label,
+  const char *reserve_reference,
+  const char *amount,
+  const char *coin_ref,
+  unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+
+  cmd = TALER_TESTING_cmd_withdraw_amount (label,
+                                           reserve_reference,
+                                           amount,
+                                           expected_response_code);
+  {
+    struct WithdrawState *ws = cmd.cls;
+
+    ws->reuse_coin_key_ref = coin_ref;
+  }
+  return cmd;
+}
+
+
 /**
  * Create withdraw command, letting the caller specify the
  * amount by a denomination key.

-- 
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]