gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (ce71db2c -> 187ae6f8)


From: gnunet
Subject: [taler-exchange] branch master updated (ce71db2c -> 187ae6f8)
Date: Mon, 13 Mar 2023 00:32:56 +0100

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

oec pushed a change to branch master
in repository exchange.

    from ce71db2c be more explicit in systemd unit file
     new 6adc2230 WiP: age-withdraw implementation, part 4/n
     new 7521ff1c -minor refactoring in age_withdraw-reveal
     new 5608a73c -simplify zero-check for age-commitment hash
     new 257f2eb9 WiP: age-withdraw, cut out work up to 
verify_commitment_and_max_age
     new e3d5672c simplify hash generation of age commitment
     new 777a4c07 Merge branch 'master' into age-withdraw
     new 9c66f270 WiP: age-withdraw, added 
TALER_age_restriction_commit_from_base, 6/n
     new 82bcd0d2 -gana bump
     new f5080a3b Merge branch 'master' into age-withdraw
     new 62da9cca -gana bump
     new 187ae6f8 WIP: age-withdraw, continue verify_commitment_and_max_age, 7/n

The 11 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 contrib/gana                                       |   2 +-
 src/exchange/taler-exchange-httpd_age-withdraw.c   |   6 +-
 .../taler-exchange-httpd_age-withdraw_reveal.c     | 283 ++++++++++++++++++---
 src/exchangedb/0003-withdraw_age_commitments.sql   |   6 +-
 src/exchangedb/pg_get_age_withdraw_info.c          |   6 +-
 src/include/taler_crypto_lib.h                     |  48 +++-
 src/include/taler_exchangedb_plugin.h              |   4 +-
 src/util/age_restriction.c                         | 268 ++++++++++++++-----
 src/util/crypto.c                                  |  35 ++-
 9 files changed, 523 insertions(+), 135 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index 1ec4596b..59de2acb 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 1ec4596bf4925ee24fc06d3e74d2a553b8239870
+Subproject commit 59de2acb7c716c816ed15786b5369e56c325770c
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c 
b/src/exchange/taler-exchange-httpd_age-withdraw.c
index 9c7703af..170cd06a 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw.c
@@ -321,8 +321,8 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
     TALER_JSON_spec_amount ("amount",
                             TEH_currency,
                             &awc.commitment.amount_with_fee),
-    GNUNET_JSON_spec_uint32 ("max_age_group",
-                             &awc.commitment.max_age_group),
+    GNUNET_JSON_spec_uint32 ("max_age",
+                             &awc.commitment.max_age),
     GNUNET_JSON_spec_end ()
   };
 
@@ -353,7 +353,7 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
     if (GNUNET_OK !=
         TALER_wallet_age_withdraw_verify (&awc.commitment.h_commitment,
                                           &awc.commitment.amount_with_fee,
-                                          awc.commitment.max_age_group,
+                                          awc.commitment.max_age,
                                           &awc.commitment.reserve_pub,
                                           &awc.commitment.reserve_sig))
     {
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c 
b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
index cdb8115f..feb16af3 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
@@ -35,7 +35,8 @@ struct AgeRevealContext
 {
 
   /**
-   * Commitment for the age-withdraw operation.
+   * Commitment for the age-withdraw operation, previously called by the
+   * client.
    */
   struct TALER_AgeWithdrawCommitmentHashP ach;
 
@@ -62,11 +63,6 @@ struct AgeRevealContext
    */
   struct TEH_DenominationKey *denom_keys;
 
-  /**
-   * #num_coins hases of blinded coins.
-   */
-  struct TALER_BlindedCoinHashP *coin_evs;
-
   /**
    * Total sum of all denominations' values
    **/
@@ -78,9 +74,14 @@ struct AgeRevealContext
   struct TALER_Amount total_fee;
 
   /**
-   * #num_coins*(kappa - 1) disclosed coins.
+   * #num_coins hashes of blinded coins.
+   */
+  struct TALER_BlindedCoinHashP *coin_evs;
+
+  /**
+   * secrets for #num_coins*(kappa - 1) disclosed coins.
    */
-  struct GNUNET_CRYPTO_EddsaPrivateKey *disclosed_coins;
+  struct TALER_PlanchetMasterSecretP *disclosed_coin_secrets;
 
   /**
    * The data from the original age-withdraw.  Will be retrieved from
@@ -98,7 +99,7 @@ age_reveal_context_free (struct AgeRevealContext *actx)
   GNUNET_free (actx->denoms_h);
   GNUNET_free (actx->denom_keys);
   GNUNET_free (actx->coin_evs);
-  GNUNET_free (actx->disclosed_coins);
+  GNUNET_free (actx->disclosed_coin_secrets);
 }
 
 
@@ -110,7 +111,7 @@ age_reveal_context_free (struct AgeRevealContext *actx)
  * @param connection The MHD connection to handle
  * @param j_denoms_h Array of hashes of the denominations for the withdrawal, 
in JSON format
  * @param j_coin_evs The blinded envelopes in JSON format for the coins that 
are not revealed and will be signed on success
- * @param j_disclosed_coins The n*(kappa-1) disclosed coins' private keys in 
JSON format, from which all other attributes (age restriction, blinding, nonce) 
will be derived from
+ * @param j_disclosed_coin_secrets The n*(kappa-1) disclosed coins' private 
keys in JSON format, from which all other attributes (age restriction, 
blinding, nonce) will be derived from
  * @param[out] actx The context of the operation, only partially built at call 
time
  * @param[out] mhd_mret The result if a reply is queued for MHD
  * @return true on success, false on failure, with a reply already queued for 
MHD.
@@ -120,7 +121,7 @@ parse_age_withdraw_reveal_json (
   struct MHD_Connection *connection,
   const json_t *j_denoms_h,
   const json_t *j_coin_evs,
-  const json_t *j_disclosed_coins,
+  const json_t *j_disclosed_coin_secrets,
   struct AgeRevealContext *actx,
   MHD_RESULT *mhd_ret)
 {
@@ -136,8 +137,8 @@ parse_age_withdraw_reveal_json (
       error = "denoms_h must be an array";
     else if (! json_is_array (j_coin_evs))
       error = "coin_evs must be an array";
-    else if (! json_is_array (j_disclosed_coins))
-      error = "disclosed_coins must be an array";
+    else if (! json_is_array (j_disclosed_coin_secrets))
+      error = "disclosed_coin_secrets must be an array";
     else if (actx->num_coins == 0)
       error = "denoms_h must not be empty";
     else if (actx->num_coins != json_array_size (j_coin_evs))
@@ -150,8 +151,8 @@ parse_age_withdraw_reveal_json (
        **/
       error = "maximum number of coins that can be withdrawn has been 
exceeded";
     else if (actx->num_coins * (TALER_CNC_KAPPA - 1)
-             != json_array_size (j_disclosed_coins))
-      error = "the size of array disclosed_coins must be "
+             != json_array_size (j_disclosed_coin_secrets))
+      error = "the size of array disclosed_coin_secrets must be "
               TALER_CNC_KAPPA_MINUS_ONE_STR " times the size of denoms_h";
 
     if (NULL != error)
@@ -222,13 +223,13 @@ parse_age_withdraw_reveal_json (
     };
 
     /* Parse diclosed keys */
-    actx->disclosed_coins = GNUNET_new_array (
+    actx->disclosed_coin_secrets = GNUNET_new_array (
       actx->num_coins * (TALER_CNC_KAPPA - 1),
-      struct GNUNET_CRYPTO_EddsaPrivateKey);
+      struct TALER_PlanchetMasterSecretP);
 
-    json_array_foreach (j_disclosed_coins, idx, value) {
+    json_array_foreach (j_disclosed_coin_secrets, idx, value) {
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_fixed_auto (NULL, &actx->disclosed_coins[idx]),
+        GNUNET_JSON_spec_fixed_auto (NULL, &actx->disclosed_coin_secrets[idx]),
         GNUNET_JSON_spec_end ()
       };
 
@@ -238,7 +239,7 @@ parse_age_withdraw_reveal_json (
         char msg[256] = {0};
         GNUNET_snprintf (msg,
                          sizeof(msg),
-                         "couldn't parse entry no. %d in array 
disclosed_coins",
+                         "couldn't parse entry no. %d in array 
disclosed_coin_secrets",
                          idx + 1);
         *mhd_ret = TALER_MHD_reply_with_error (connection,
                                                MHD_HTTP_BAD_REQUEST,
@@ -305,7 +306,7 @@ find_original_commitment (
     break;
 
   case GNUNET_DB_STATUS_SOFT_ERROR:
-  /* FIXME: Do we queue a result in this case or retry? */
+  /* FIXME:oec: Do we queue a result in this case or retry? */
   default:
     GNUNET_break (0);       /* should be impossible */
     *result = TALER_MHD_reply_with_error (connection,
@@ -343,17 +344,15 @@ denomination_is_valid (
     connection,
     result);
 
-  /* Does the denomination exist? */
   if (NULL == dks)
   {
+    /* The denomination doesn't exist */
     GNUNET_assert (result != NULL);
     /* Note: a HTTP-response has been queued and result has been set by
      * TEH_keys_denominations_by_hash2 */
     return false;
   }
 
-  /* Is the denomation still and already valid? */
-
   if (GNUNET_TIME_absolute_is_past (dks->meta.expire_withdraw.abs_time))
   {
     /* This denomination is past the expiration time for withdraws */
@@ -504,7 +503,7 @@ are_denominations_valid (
 
     if (0 != TALER_amount_cmp (&sum, amount_with_fee))
     {
-      GNUNET_break (0);
+      GNUNET_break_op (0);
       *result = TALER_MHD_reply_with_ec (connection,
                                          
TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT,
                                          NULL);
@@ -516,6 +515,213 @@ are_denominations_valid (
 }
 
 
+/**
+ * Checks the validity of the disclosed coins as follows:
+ * - Derives and calculates the disclosed coins'
+ *    - public keys,
+ *    - nonces (if applicable),
+ *    - age commitments,
+ *    - blindings
+ *    - blinded hashes
+ * - Computes h_commitment with those calculated and the undisclosed hashes
+ * - Compares h_commitment with the value from the original commitment
+ * - Verifies that all public keys in indices larger than the age group
+ *   corresponding to max_age are derived from the constant public key.
+ *
+ * The derivation of the blindings, (potential) nonces and age-commitment from
+ * a coin's private keys is defined in
+ * https://docs.taler.net/design-documents/024-age-restriction.html#withdraw
+ *
+ * @param connection HTTP-connection to the client
+ * @param h_commitment_orig Original commitment
+ * @param max_age Maximum age allowed for the age restriction
+ * @param noreveal_idx Index that was given to the client in response to the 
age-withdraw request
+ * @param num_coins Number of coins
+ * @param coin_evs The Hashes of the undisclosed, blinded coins, @a num_coins 
many
+ * @param denom_keys The array of denomination keys, @a num_coins. Needed to 
detect Clause-Schnorr-based denominations
+ * @param discloded_coin_secrets The secrets of the disclosed coins, 
(TALER_CNC_KAPPA - 1)*num_coins many
+ * @param[out] result On error, a HTTP-response will be queued and result set 
accordingly
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+static enum GNUNET_GenericReturnValue
+verify_commitment_and_max_age (
+  struct MHD_Connection *connection,
+  const struct TALER_AgeWithdrawCommitmentHashP *h_commitment_orig,
+  const uint32_t max_age,
+  const uint32_t noreveal_idx,
+  const uint32_t num_coins,
+  const struct TALER_BlindedCoinHashP *coin_evs,
+  const struct TEH_DenominationKey *denom_keys,
+  const struct TALER_PlanchetMasterSecretP *disclosed_coin_secrets,
+  MHD_RESULT *result)
+{
+  enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
+  struct GNUNET_HashContext *hash_context;
+
+  hash_context = GNUNET_CRYPTO_hash_context_start ();
+
+  for (size_t c = 0; c < num_coins; c++)
+  {
+    size_t k = 0; /* either 0 or 1, to index into coin_evs */
+
+    for (size_t idx = 0; idx<3; idx++)
+    {
+      if (idx == (size_t) noreveal_idx)
+      {
+        GNUNET_CRYPTO_hash_context_read (hash_context,
+                                         &coin_evs[c],
+                                         sizeof(coin_evs[c]));
+      }
+      else
+      {
+        /* FIXME:oec: Refactor this block out into its own function */
+
+        size_t j = 2 * c + k; /* Index into disclosed_coin_secrets[] */
+        const struct TALER_PlanchetMasterSecretP *secret;
+        struct TALER_AgeCommitmentHash ach;
+
+        GNUNET_assert (k<2);
+        GNUNET_assert (num_coins * (TALER_CNC_KAPPA - 1) > j);
+
+        secret = &disclosed_coin_secrets[j];
+        k++;
+
+        /* First: calculate age commitment hash */
+        {
+          struct TALER_AgeCommitmentProof acp;
+          ret = TALER_age_restriction_from_secret (
+            secret,
+            &denom_keys[c].denom_pub.age_mask,
+            max_age,
+            &acp);
+
+          if (GNUNET_OK != ret)
+          {
+            GNUNET_break (0);
+            *result = TALER_MHD_reply_json_pack (connection,
+                                                 
MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                                 "{sssi}",
+                                                 "failed to derive age 
restriction from base key",
+                                                 "index",
+                                                 j);
+            return ret;
+          }
+
+          TALER_age_commitment_hash (&acp.commitment, &ach);
+        }
+
+        /* Next: calculate planchet */
+        {
+          struct TALER_CoinPubHashP c_hash;
+          struct TALER_PlanchetDetail detail;
+          struct TALER_BlindedCoinHashP bch;
+          struct TALER_CoinSpendPrivateKeyP coin_priv;
+          union TALER_DenominationBlindingKeyP bks;
+          struct TALER_ExchangeWithdrawValues alg_values = {
+            .cipher = denom_keys[c].denom_pub.cipher,
+          };
+
+          if (TALER_DENOMINATION_CS == alg_values.cipher)
+          {
+            struct TALER_CsNonce nonce;
+
+            TALER_cs_withdraw_nonce_derive (
+              secret,
+              &nonce);
+
+            {
+              enum TALER_ErrorCode ec;
+              struct TEH_CsDeriveData cdd = {
+                .h_denom_pub = &denom_keys[c].h_denom_pub,
+                .nonce = &nonce,
+              };
+
+              ec = TEH_keys_denomination_cs_r_pub (&cdd,
+                                                   false,
+                                                   &alg_values.details.
+                                                   cs_values);
+
+#pragma message ("FIXME:oec: return value of needs handling!")
+              /* FIXME:oec: Handle error */
+              GNUNET_assert (TALER_EC_NONE == ec);
+            }
+          }
+
+          TALER_planchet_blinding_secret_create (secret,
+                                                 &alg_values,
+                                                 &bks);
+
+          TALER_planchet_setup_coin_priv (secret,
+                                          &alg_values,
+                                          &coin_priv);
+
+          ret = TALER_planchet_prepare (&denom_keys[c].denom_pub,
+                                        &alg_values,
+                                        &bks,
+                                        &coin_priv,
+                                        &ach,
+                                        &c_hash,
+                                        &detail);
+
+          if (GNUNET_OK != ret)
+          {
+            GNUNET_break (0);
+            *result = TALER_MHD_reply_json_pack (connection,
+                                                 
MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                                 "{sssi}",
+                                                 "details",
+                                                 "failed to prepare planchet 
from base key",
+                                                 "index",
+                                                 j);
+            return ret;
+          }
+
+          ret = TALER_coin_ev_hash (&detail.blinded_planchet,
+                                    &denom_keys[c].h_denom_pub,
+                                    &bch);
+          if (GNUNET_OK != ret)
+          {
+            GNUNET_break (0);
+            *result = TALER_MHD_reply_json_pack (connection,
+                                                 
MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                                 "{sssi}",
+                                                 "details",
+                                                 "failed to hash planchet from 
base key",
+                                                 "index",
+                                                 j);
+            return ret;
+          }
+
+          GNUNET_CRYPTO_hash_context_read (hash_context,
+                                           &detail.blinded_planchet,
+                                           sizeof(detail.blinded_planchet));
+        }
+      }
+    }
+  }
+
+  /* Finally, compare the calculated hash with the original commitment */
+  {
+    struct GNUNET_HashCode calc_hash;
+    GNUNET_CRYPTO_hash_context_finish (hash_context,
+                                       &calc_hash);
+
+    if (0 != GNUNET_CRYPTO_hash_cmp (&h_commitment_orig->hash,
+                                     &calc_hash))
+    {
+      GNUNET_break_op (0);
+      *result = TALER_MHD_reply_with_ec (connection,
+                                         
TALER_EC_EXCHANGE_AGE_WITHDRAW_REVEAL_INVALID_HASH,
+                                         NULL);
+      return GNUNET_SYSERR;
+    }
+
+  }
+
+  return ret;
+}
+
+
 MHD_RESULT
 TEH_handler_age_withdraw_reveal (
   struct TEH_RequestContext *rc,
@@ -527,12 +733,12 @@ TEH_handler_age_withdraw_reveal (
   struct AgeRevealContext actx = {0};
   json_t *j_denoms_h;
   json_t *j_coin_evs;
-  json_t *j_disclosed_coins;
+  json_t *j_disclosed_coin_secrets;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("reserve_pub", &actx.reserve_pub),
     GNUNET_JSON_spec_json ("denoms_h", &j_denoms_h),
     GNUNET_JSON_spec_json ("coin_evs", &j_coin_evs),
-    GNUNET_JSON_spec_json ("disclosed_coins", &j_disclosed_coins),
+    GNUNET_JSON_spec_json ("disclosed_coin_secrets", 
&j_disclosed_coin_secrets),
     GNUNET_JSON_spec_end ()
   };
 
@@ -557,7 +763,7 @@ TEH_handler_age_withdraw_reveal (
           rc->connection,
           j_denoms_h,
           j_coin_evs,
-          j_disclosed_coins,
+          j_disclosed_coin_secrets,
           &actx,
           &result))
       break;
@@ -583,14 +789,23 @@ TEH_handler_age_withdraw_reveal (
           &result))
       break;
 
+    /* Verify the computed h_commitment equals the committed one and that
+     * coins have a maximum age group corresponding max_age (age-mask 
dependend) */
+    if (GNUNET_OK != verify_commitment_and_max_age (
+          rc->connection,
+          &actx.commitment.h_commitment,
+          actx.commitment.max_age,
+          actx.commitment.noreveal_index,
+          actx.num_coins,
+          actx.coin_evs,
+          actx.denom_keys,
+          actx.disclosed_coin_secrets,
+          &result))
+      break;
 
-  } while(0);
+    /* TODO:oec: sign the coins */
 
-  /* TODO:oec: compute the disclosed blinded coins */
-  /* TODO:oec: generate h_commitment_comp */
-  /* TODO:oec: compare h_commitment_comp against h_commitment */
-  /* TODO:oec: sign the coins */
-  /* TODO:oec: send response */
+  } while(0);
 
   age_reveal_context_free (&actx);
   GNUNET_JSON_parse_free (spec);
diff --git a/src/exchangedb/0003-withdraw_age_commitments.sql 
b/src/exchangedb/0003-withdraw_age_commitments.sql
index 6064880b..6c153598 100644
--- a/src/exchangedb/0003-withdraw_age_commitments.sql
+++ b/src/exchangedb/0003-withdraw_age_commitments.sql
@@ -29,7 +29,7 @@ BEGIN
       ',h_commitment BYTEA PRIMARY KEY CHECK (LENGTH(h_commitment)=64)'
       ',amount_with_fee_val INT8 NOT NULL'
       ',amount_with_fee_frac INT4 NOT NULL'
-      ',max_age_group INT2 NOT NULL'
+      ',max_age INT2 NOT NULL'
       ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
       ',reserve_sig BYTEA CHECK (LENGTH(reserve_sig)=64)'
       ',noreveal_index INT4 NOT NULL'
@@ -51,8 +51,8 @@ BEGIN
     ,partition_suffix
   );
   PERFORM comment_partitioned_column(
-     'The maximum age group that the client commits to with this request'
-    ,'max_age_group'
+     'The maximum age that the client commits to with this request'
+    ,'max_age'
     ,table_name
     ,partition_suffix
   );
diff --git a/src/exchangedb/pg_get_age_withdraw_info.c 
b/src/exchangedb/pg_get_age_withdraw_info.c
index 7662d4b5..02ccc84a 100644
--- a/src/exchangedb/pg_get_age_withdraw_info.c
+++ b/src/exchangedb/pg_get_age_withdraw_info.c
@@ -45,8 +45,8 @@ TEH_PG_get_age_withdraw_info (
                                           &awc->reserve_sig),
     GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
                                           &awc->reserve_pub),
-    GNUNET_PQ_result_spec_uint32 ("max_age_group",
-                                  &awc->max_age_group),
+    GNUNET_PQ_result_spec_uint32 ("max_age",
+                                  &awc->max_age),
     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
                                  &awc->amount_with_fee),
     GNUNET_PQ_result_spec_uint32 ("noreveal_index",
@@ -66,7 +66,7 @@ TEH_PG_get_age_withdraw_info (
            " h_commitment"
            ",reserve_sig"
            ",reserve_pub"
-           ",max_age_group"
+           ",max_age"
            ",amount_with_fee_val"
            ",amount_with_fee_frac"
            ",noreveal_index"
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index db50efa1..5aa22b53 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -18,6 +18,7 @@
  * @brief taler-specific crypto functions
  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
  * @author Christian Grothoff <christian@grothoff.org>
+ * @author Özgür Kesim <oec-taler@kesim.org>
  */
 #if ! defined (__TALER_UTIL_LIB_H_INSIDE__)
 #error "Only <taler_util.h> can be included directly."
@@ -1309,13 +1310,8 @@ struct TALER_AgeAttestation
 #endif
 };
 
-
-extern const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash;
-#define TALER_AgeCommitmentHash_isNullOrZero(ph) \
-  ((NULL == ph) ||                               \
-   (0 == memcmp (ph,                             \
-                 &TALER_ZeroAgeCommitmentHash,   \
-                 sizeof(struct TALER_AgeCommitmentHash))))
+#define TALER_AgeCommitmentHash_isNullOrZero(ph) ((NULL == ph) || \
+                                                  GNUNET_is_zero (ph))
 
 /**
  * @brief Type of public signing keys for verifying blindly signed coins.
@@ -5931,4 +5927,42 @@ void
 TALER_age_commitment_proof_free (
   struct TALER_AgeCommitmentProof *p);
 
+
+/**
+ * @brief For age-withdraw, clients have to prove that the public keys for all
+ * age groups larger than the allowed maximum age group are derived by scalar
+ * multiplication from this Edx25519 public key (in Crockford Base32 encoding):
+ *
+ *       DZJRF6HXN520505XDAWM8NMH36QV9J3VH77265WQ09EBQ76QSKCG
+ *
+ * Its private key was chosen randomly and then deleted.
+ */
+extern struct
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+GNUNET_CRYPTO_Edx25519PublicKey
+#else
+GNUNET_CRYPTO_EcdsaPublicKey
+#endif
+TALER_age_commitment_base_public_key;
+
+/**
+ * @brief Similiar to TALER_age_restriction_commit, but takes the coin's
+ * private key as seed input and calculates the public keys in the slots larger
+ * than the given age as derived from TALER_age_commitment_base_public_key.
+ *
+ * See 
https://docs.taler.net/core/api-exchange.html#withdraw-with-age-restriction
+ *
+ * @param secret The master secret of the coin from which we derive the age 
restriction
+ * @param mask The age mask, defining the age groups
+ * @param max_age The maximum age for this coin.
+ * @param[out] comm_proof The commitment and proof for age restriction for age 
@a max_age
+ */
+enum GNUNET_GenericReturnValue
+TALER_age_restriction_from_secret (
+  const struct TALER_PlanchetMasterSecretP *secret,
+  const struct TALER_AgeMask *mask,
+  const uint8_t max_age,
+  struct TALER_AgeCommitmentProof *comm_proof);
+
+
 #endif
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 2c606225..e05ddeed 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1064,9 +1064,9 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment
   struct TALER_Amount amount_with_fee;
 
   /**
-   * Maximum age group that the coins are restricted to.
+   * Maximum age that the coins are restricted to.
    */
-  uint32_t max_age_group;
+  uint32_t max_age;
 
   /**
    * The hash of the commitment of all n*kappa coins
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index f4ac9abe..beb68e5a 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2022 Taler Systems SA
+  Copyright (C) 2022-2023 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -24,6 +24,19 @@
 #include <gnunet/gnunet_json_lib.h>
 #include <gcrypt.h>
 
+struct
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+GNUNET_CRYPTO_Edx25519PublicKey
+#else
+GNUNET_CRYPTO_EcdsaPublicKey
+#endif
+TALER_age_commitment_base_public_key = {
+  .q_y = { 0x6f, 0xe5, 0x87, 0x9a, 0x3d, 0xa9, 0x44, 0x20,
+           0x80, 0xbd, 0x6a, 0xb9, 0x44, 0x56, 0x91, 0x19,
+           0xaf, 0xb4, 0xc8, 0x7b, 0x89, 0xce, 0x23, 0x17,
+           0x97, 0x20, 0x5c, 0xbb, 0x9c, 0xd7, 0xcc, 0xd9},
+};
+
 void
 TALER_age_commitment_hash (
   const struct TALER_AgeCommitment *commitment,
@@ -82,36 +95,78 @@ get_age_group (
 }
 
 
+#ifdef AGE_RESTRICTION_WITH_ECDSA
+/* @brief Helper function to generate a ECDSA private key
+ *
+ * @param seed Input seed
+ * @param size Size of the seed in bytes
+ * @param[out] pkey ECDSA private key
+ * @return GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+ecdsa_create_from_seed (
+  const void *seed,
+  size_t seed_size,
+  struct GNUNET_CRYPTO_EcdsaPrivateKey *key)
+{
+  enum GNUNET_GenericReturnValue ret;
+  ret = GNUNET_CRYPTO_kdf (key,
+                           sizeof (*key),
+                           &seed,
+                           seed_size,
+                           "age commitment",
+                           sizeof ("age commitment") - 1,
+                           NULL, 0);
+  if (GNUNET_OK != ret)
+    return ret;
+
+  /* See GNUNET_CRYPTO_ecdsa_key_create */
+  key->d[0] &= 248;
+  key->d[31] &= 127;
+  key->d[31] |= 64;
+
+  return GNUNET_OK;
+}
+
+
+#endif
+
+
 enum GNUNET_GenericReturnValue
 TALER_age_restriction_commit (
   const struct TALER_AgeMask *mask,
   const uint8_t age,
   const struct GNUNET_HashCode *seed,
-  struct TALER_AgeCommitmentProof *new)
+  struct TALER_AgeCommitmentProof *ncp)
 {
   struct GNUNET_HashCode seed_i;
-  uint8_t num_pub = __builtin_popcount (mask->bits) - 1;
-  uint8_t num_priv = get_age_group (mask, age);
+  uint8_t num_pub;
+  uint8_t num_priv;
   size_t i;
 
+  GNUNET_assert (NULL != mask);
   GNUNET_assert (NULL != seed);
-  GNUNET_assert (NULL != new);
+  GNUNET_assert (NULL != ncp);
   GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
+
+  num_pub = __builtin_popcount (mask->bits) - 1;
+  num_priv = get_age_group (mask, age);
+
   GNUNET_assert (31 > num_priv);
   GNUNET_assert (num_priv <= num_pub);
 
   seed_i = *seed;
-  new->commitment.mask.bits = mask->bits;
-  new->commitment.num = num_pub;
-  new->proof.num = num_priv;
-  new->proof.keys = NULL;
+  ncp->commitment.mask.bits = mask->bits;
+  ncp->commitment.num = num_pub;
+  ncp->proof.num = num_priv;
+  ncp->proof.keys = NULL;
 
-  new->commitment.keys = GNUNET_new_array (
+  ncp->commitment.keys = GNUNET_new_array (
     num_pub,
     struct TALER_AgeCommitmentPublicKeyP);
 
   if (0 < num_priv)
-    new->proof.keys = GNUNET_new_array (
+    ncp->proof.keys = GNUNET_new_array (
       num_priv,
       struct TALER_AgeCommitmentPrivateKeyP);
 
@@ -126,47 +181,33 @@ TALER_age_restriction_commit (
 
     /* Only save the private keys for age groups less than num_priv */
     if (i < num_priv)
-      pkey = &new->proof.keys[i];
+      pkey = &ncp->proof.keys[i];
 
 #ifndef AGE_RESTRICTION_WITH_ECDSA
     GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
                                                  sizeof(seed_i),
                                                  &pkey->priv);
     GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
-                                           &new->commitment.keys[i].pub);
-    seed_i.bits[0] += 1;
-  }
-
-  return GNUNET_OK;
+                                           &ncp->commitment.keys[i].pub);
 #else
-    if  (GNUNET_OK !=
-         GNUNET_CRYPTO_kdf (pkey,
-                            sizeof (*pkey),
-                            &salti,
-                            sizeof (salti),
-                            "age commitment",
-                            strlen ("age commitment"),
-                            NULL, 0))
-      goto FAIL;
-
-    /* See GNUNET_CRYPTO_ecdsa_key_create */
-    pkey->priv.d[0] &= 248;
-    pkey->priv.d[31] &= 127;
-    pkey->priv.d[31] |= 64;
+    if (GNUNET_OK !=
+        ecdsa_create_from_seed (&seed_i,
+                                sizeof(seed_i),
+                                &pkey->priv))
+    {
+      GNUNET_free (ncp->commitment.keys);
+      GNUNET_free (ncp->proof.keys);
+      return GNUNET_SYSERR;
+    }
 
     GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
-                                        &new->commitment.keys[i].pub);
+                                        &ncp->commitment.keys[i].pub);
+#endif
 
+    seed_i.bits[0] += 1;
   }
 
   return GNUNET_OK;
-
-FAIL:
-  GNUNET_free (new->commitment.keys);
-  if (NULL != new->proof.keys)
-    GNUNET_free (new->proof.keys);
-  return GNUNET_SYSERR;
-#endif
 }
 
 
@@ -216,33 +257,30 @@ TALER_age_commitment_derive (
       &newacp->proof.keys[i].priv);
   }
 #else
-  char label[sizeof(uint64_t) + 1] = {0};
-
-  /* Because GNUNET_CRYPTO_ecdsa_public_key_derive expects char * (and calls
-   * strlen on it), we must avoid 0's in the label.  */
-  uint64_t nz_salt = salt | 0x8040201008040201;
-  memcpy (label, &nz_salt, sizeof(nz_salt));
-
-  /* 1. Derive the public keys */
-  for (size_t i = 0; i < orig->commitment.num; i++)
   {
-    GNUNET_CRYPTO_ecdsa_public_key_derive (
-      &orig->commitment.keys[i].pub,
-      label,
-      "age commitment derive",
-      &newacp->commitment.keys[i].pub);
-  }
+    const char *label = GNUNET_h2s (salt);
 
-  /* 2. Derive the private keys */
-  for (size_t i = 0; i < orig->proof.num; i++)
-  {
-    struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
-    priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
-      &orig->proof.keys[i].priv,
-      label,
-      "age commitment derive");
-    newacp->proof.keys[i].priv = *priv;
-    GNUNET_free (priv);
+    /* 1. Derive the public keys */
+    for (size_t i = 0; i < orig->commitment.num; i++)
+    {
+      GNUNET_CRYPTO_ecdsa_public_key_derive (
+        &orig->commitment.keys[i].pub,
+        label,
+        "age commitment derive",
+        &newacp->commitment.keys[i].pub);
+    }
+
+    /* 2. Derive the private keys */
+    for (size_t i = 0; i < orig->proof.num; i++)
+    {
+      struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
+      priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
+        &orig->proof.keys[i].priv,
+        label,
+        "age commitment derive");
+      newacp->proof.keys[i].priv = *priv;
+      GNUNET_free (priv);
+    }
   }
 #endif
 
@@ -546,4 +584,106 @@ TALER_age_mask_to_string (
 }
 
 
+enum GNUNET_GenericReturnValue
+TALER_age_restriction_from_secret (
+  const struct TALER_PlanchetMasterSecretP *secret,
+  const struct TALER_AgeMask *mask,
+  const uint8_t max_age,
+  struct TALER_AgeCommitmentProof *ncp)
+{
+  struct GNUNET_HashCode seed_i = {0};
+  uint8_t num_pub;
+  uint8_t num_priv;
+
+  GNUNET_assert (NULL != mask);
+  GNUNET_assert (NULL != secret);
+  GNUNET_assert (NULL != ncp);
+  GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
+
+  num_pub = __builtin_popcount (mask->bits) - 1;
+  num_priv = get_age_group (mask, max_age);
+
+  GNUNET_assert (31 > num_priv);
+  GNUNET_assert (num_priv <= num_pub);
+
+  ncp->commitment.mask.bits = mask->bits;
+  ncp->commitment.num = num_pub;
+  ncp->proof.num = num_priv;
+  ncp->proof.keys = NULL;
+
+  ncp->commitment.keys = GNUNET_new_array (
+    num_pub,
+    struct TALER_AgeCommitmentPublicKeyP);
+
+  if (0 < num_priv)
+    ncp->proof.keys = GNUNET_new_array (
+      num_priv,
+      struct TALER_AgeCommitmentPrivateKeyP);
+
+  /* Create as many private keys as allow with max_age and derive the
+   * corresponding public keys.  The rest of the needed public keys are created
+   * by scalar mulitplication with the TALER_age_commitment_base_public_key. */
+  for (size_t i = 0; i < num_pub; i++)
+  {
+    enum GNUNET_GenericReturnValue ret;
+    const char *label = i < num_priv ? "age-commitment" : "age-factor";
+
+    ret = GNUNET_CRYPTO_kdf (&seed_i, sizeof(seed_i),
+                             secret, sizeof(*secret),
+                             label, strlen (label),
+                             &i, sizeof(i),
+                             NULL, 0);
+    GNUNET_assert (GNUNET_OK == ret);
+
+    /* Only generate and save the private keys and public keys for age groups
+     * less than num_priv */
+    if (i < num_priv)
+    {
+      struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.keys[i];
+
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+      GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
+                                                   sizeof(seed_i),
+                                                   &pkey->priv);
+      GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
+                                             &ncp->commitment.keys[i].pub);
+#else
+      if (GNUNET_OK != ecdsa_create_from_seed (&seed_i,
+                                               sizeof(seed_i),
+                                               &pkey->priv))
+      {
+        GNUNET_free (ncp->commitment.keys);
+        GNUNET_free (ncp->proof.keys);
+        return GNUNET_SYSERR;
+      }
+      GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
+                                          &ncp->commitment.keys[i].pub);
+#endif
+    }
+    else
+    {
+      /* For all indices larger than num_priv, derive a public key from
+       * TALER_age_commitment_base_public_key by scalar multiplication */
+#ifndef AGE_RESTRICTION_WITH_ECDSA
+      GNUNET_CRYPTO_edx25519_public_key_derive (
+        &TALER_age_commitment_base_public_key,
+        &seed_i,
+        sizeof(seed_i),
+        &ncp->commitment.keys[i].pub);
+#else
+
+      GNUNET_CRYPTO_ecdsa_public_key_derive (
+        &TALER_age_commitment_base_public_key,
+        GNUNET_h2s (&seed_i),
+        "age withdraw",
+        &ncp->commitment.keys[i].pub);
+#endif
+    }
+  }
+
+  return GNUNET_OK;
+
+}
+
+
 /* end util/age_restriction.c */
diff --git a/src/util/crypto.c b/src/util/crypto.c
index 5cbba813..bb14b6cd 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -26,11 +26,6 @@
 #include "taler_util.h"
 #include <gcrypt.h>
 
-/**
- * Used in TALER_AgeCommitmentHash_isNullOrZero for comparison
- */
-const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash = {0};
-
 /**
  * Function called by libgcrypt on serious errors.
  * Prints an error message and aborts the process.
@@ -426,19 +421,23 @@ TALER_coin_pub_hash (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
   {
     /* Coin comes with age commitment.  Take the hash of the age commitment
      * into account */
-    const size_t key_s = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey);
-    const size_t age_s = sizeof(struct TALER_AgeCommitmentHash);
-    char data[key_s + age_s];
-
-    GNUNET_memcpy (&data[0],
-                   &coin_pub->eddsa_pub,
-                   key_s);
-    GNUNET_memcpy (&data[key_s],
-                   ach,
-                   age_s);
-    GNUNET_CRYPTO_hash (&data,
-                        key_s + age_s,
-                        &coin_h->hash);
+    struct GNUNET_HashContext *hash_context;
+
+    hash_context = GNUNET_CRYPTO_hash_context_start ();
+
+    GNUNET_CRYPTO_hash_context_read (
+      hash_context,
+      &coin_pub->eddsa_pub,
+      sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
+
+    GNUNET_CRYPTO_hash_context_read (
+      hash_context,
+      ach,
+      sizeof(struct TALER_AgeCommitmentHash));
+
+    GNUNET_CRYPTO_hash_context_finish (
+      hash_context,
+      &coin_h->hash);
   }
 }
 

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