gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (eb4c765e -> 9865febb)


From: gnunet
Subject: [taler-exchange] branch master updated (eb4c765e -> 9865febb)
Date: Sun, 26 Jun 2022 17:15:01 +0200

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

oec pushed a change to branch master
in repository exchange.

    from eb4c765e -more doxygen
     new 140a54ed -cleanup comments
     new b39febe3 -fix/rename this -> new
     new 31f74059 [new /keys response] create and parse denomination implemented
     new ce515a1f -make econtract optional as per design
     new ea21572b -fix path
     new 5575194a -fix path
     new 646c410a -add auditor_priv_file
     new fd9fc9f0 -fix misc doxygen warnings, code clean up
     new 2508d4bb -add missing comments
     new 7b62174d -fix typos
     new 2443ee67 -more typos
     new 372a103a -more doxygen
     new 9865febb Merge branch 'master' of ssh://git.taler.net/exchange

The 13 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:
 src/exchange/taler-exchange-httpd_keys.c   | 161 +++++++++---------
 src/extensions/extension_age_restriction.c |  54 +++---
 src/include/taler_json_lib.h               |  44 +++++
 src/json/json_helper.c                     | 188 ++++++++++++++++++++-
 src/json/json_pack.c                       |  23 +--
 src/lib/exchange_api_handle.c              | 257 +++++++++++++----------------
 6 files changed, 468 insertions(+), 259 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index 6eadb0d7..de5f1fbc 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -1351,7 +1351,7 @@ denomination_info_cb (
   dk->meta = *meta;
   dk->master_sig = *master_sig;
   dk->recoup_possible = recoup_possible;
-  dk->denom_pub.age_mask = meta->age_mask; /* FIXME-oec: age_mask -> 
reserved_field */
+  dk->denom_pub.age_mask = meta->age_mask;
 
   GNUNET_assert (
     GNUNET_OK ==
@@ -1726,12 +1726,13 @@ setup_general_response_headers (struct 
TEH_KeyStateHandle *ksh,
  * @a recoup and @a denoms.
  *
  * @param[in,out] ksh key state handle we build @a krd for
- * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms
+ * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms
  * @param last_cpd timestamp to use
  * @param signkeys list of sign keys to return
  * @param recoup list of revoked keys to return
  * @param denoms list of denominations to return
  * @param grouped_denominations list of grouped denominations to return
+ * @param[in] h_grouped XOR of all hashes in @a grouped_demoninations
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
@@ -1741,11 +1742,14 @@ create_krd (struct TEH_KeyStateHandle *ksh,
             json_t *signkeys,
             json_t *recoup,
             json_t *denoms,
-            json_t *grouped_denominations)
+            json_t *grouped_denominations,
+            const struct GNUNET_HashCode *h_grouped)
 {
   struct KeysResponseData krd;
   struct TALER_ExchangePublicKeyP exchange_pub;
   struct TALER_ExchangeSignatureP exchange_sig;
+  struct TALER_ExchangePublicKeyP grouped_exchange_pub;
+  struct TALER_ExchangeSignatureP grouped_exchange_sig;
   json_t *keys;
 
   GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time));
@@ -1753,11 +1757,13 @@ create_krd (struct TEH_KeyStateHandle *ksh,
   GNUNET_assert (NULL != recoup);
   GNUNET_assert (NULL != denoms);
   GNUNET_assert (NULL != grouped_denominations);
+  GNUNET_assert (NULL != h_grouped);
   GNUNET_assert (NULL != ksh->auditors);
   GNUNET_assert (NULL != TEH_currency);
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Creating /keys at cherry pick date %s\n",
               GNUNET_TIME_timestamp2s (last_cpd));
+
   /* Sign hash over denomination keys */
   {
     enum TALER_ErrorCode ec;
@@ -1779,6 +1785,33 @@ create_krd (struct TEH_KeyStateHandle *ksh,
     }
   }
 
+  /* Sign grouped hash */
+  {
+    enum TALER_ErrorCode ec;
+
+    if (TALER_EC_NONE !=
+        (ec =
+           TALER_exchange_online_key_set_sign (
+             &TEH_keys_exchange_sign2_,
+             ksh,
+             last_cpd,
+             h_grouped,
+             &grouped_exchange_pub,
+             &grouped_exchange_sig)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Could not create key response data: cannot sign grouped 
hash (%s)\n",
+                  TALER_ErrorCode_get_hint (ec));
+      return GNUNET_SYSERR;
+    }
+  }
+
+  /* both public keys really must be the same */
+  GNUNET_assert (0 ==
+                 memcmp (&grouped_exchange_pub,
+                         &exchange_pub,
+                         sizeof(exchange_pub)));
+
   {
     const struct SigningKey *sk;
 
@@ -1815,7 +1848,9 @@ create_krd (struct TEH_KeyStateHandle *ksh,
     GNUNET_JSON_pack_data_auto ("eddsa_pub",
                                 &exchange_pub),
     GNUNET_JSON_pack_data_auto ("eddsa_sig",
-                                &exchange_sig));
+                                &exchange_sig),
+    GNUNET_JSON_pack_data_auto ("denominations_sig",
+                                &grouped_exchange_sig));
   GNUNET_assert (NULL != keys);
 
   /* Set wallet limit if KYC is configured */
@@ -1876,7 +1911,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
       GNUNET_assert (0 == r);
     }
 
-    /* Update the keys object with the extensions */
+    /* Update the keys object with the extensions and its signature */
     if (has_extensions)
     {
       json_t *sig;
@@ -1888,12 +1923,10 @@ create_krd (struct TEH_KeyStateHandle *ksh,
         extensions);
       GNUNET_assert (0 == r);
 
-      /* add extensions_sig */
       sig = GNUNET_JSON_PACK (
         GNUNET_JSON_pack_data_auto ("extensions_sig",
                                     &TEH_extensions_sig));
 
-      /* update the keys object with extensions_sig */
       r = json_object_update (keys, sig);
       GNUNET_assert (0 == r);
     }
@@ -2000,6 +2033,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
   struct GNUNET_TIME_Timestamp last_cpd;
   struct GNUNET_CONTAINER_Heap *heap;
   struct GNUNET_HashContext *hash_context = NULL;
+  struct GNUNET_HashCode grouped_hash_xor = {0};
 
   sctx.signkeys = json_array ();
   GNUNET_assert (NULL != sctx.signkeys);
@@ -2045,8 +2079,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
     /* groupData is the value we store for each group meta-data */
     struct groupData
     {
-      json_t *json;   /* The json blob with the group meta-data and list of 
denominations */
-      struct GNUNET_HashContext *hash_context;   /* hash over all 
denominations in that group */
+      /* The json blob with the group meta-data and list of denominations */
+      json_t *json;
+
+      /* xor of all hashes of denominations in that group */
+      struct GNUNET_HashCode hash_xor;
     };
 
     /* heap = min heap, sorted by start time */
@@ -2060,13 +2097,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
         /*
          * This is not the first entry in the heap (because last_cpd !=
          * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different
-         * start time.  Therefore, we create an entry in the ksh.
+         * start time.  Therefore, we create a new entry in ksh.
          */
         struct GNUNET_HashCode hc;
 
         GNUNET_CRYPTO_hash_context_finish (
           GNUNET_CRYPTO_hash_context_copy (hash_context),
           &hc);
+
         if (GNUNET_OK !=
             create_krd (ksh,
                         &hc,
@@ -2074,7 +2112,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
                         sctx.signkeys,
                         recoup,
                         denoms,
-                        grouped_denominations))
+                        grouped_denominations,
+
+                        &grouped_hash_xor))
         {
           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                       "Failed to generate key response data for %s\n",
@@ -2135,54 +2175,43 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
        * denominations in this group as a json-blob in the multihashmap
        * denominations_by_group.
        **/
-
       {
         static const char *denoms_key = "denoms";
         struct groupData *group;
         json_t *list;
         json_t *entry;
         struct GNUNET_HashCode key;
-
-        /* Find the group/JSON-blob for the key */
-        struct
-        {
-          enum TALER_DenominationCipher cipher;
-          struct TALER_AgeMask age_mask;
-          struct TALER_Amount value;
-          struct TALER_DenomFeeSet fees;
-        } meta = {
+        struct TALER_DenominationGroup meta = {
           .cipher = dk->denom_pub.cipher,
           .value = dk->meta.value,
           .fees = dk->meta.fees,
           .age_mask = dk->meta.age_mask,
         };
 
+        /* Search the group/JSON-blob for the key */
         GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key);
 
-        group = (struct groupData *) GNUNET_CONTAINER_multihashmap_get (
-          denominations_by_group,
-          &key);
+        group =
+          (struct groupData *) GNUNET_CONTAINER_multihashmap_get (
+            denominations_by_group,
+            &key);
 
         if (NULL == group)
         {
-          /*
-           * There is no group for this meta-data yet, so let's create a new
-           * group entry.
-           */
-
+          // There is no group for this meta-data yet, so we create a new group
           bool age_restricted = meta.age_mask.bits != 0;
           char *cipher;
 
           group = GNUNET_new (struct groupData);
-          group->hash_context = GNUNET_CRYPTO_hash_context_start ();
+          memset (group, 0, sizeof(*group));
 
           switch (meta.cipher)
           {
           case TALER_DENOMINATION_RSA:
-            cipher = age_restricted ? "RSA+age_restriction": "RSA";
+            cipher = age_restricted ? "RSA+age_restricted": "RSA";
             break;
           case TALER_DENOMINATION_CS:
-            cipher = age_restricted ? "CS+age_restriction": "CS";
+            cipher = age_restricted ? "CS+age_restricted": "CS";
             break;
           default:
             GNUNET_assert (false);
@@ -2196,13 +2225,13 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
 
           if (age_restricted)
           {
-            GNUNET_assert (0 ==
-                           json_object_set (group->json,
-                                            "age_mask",
-                                            json_integer 
(meta.age_mask.bits)));
+            int r = json_object_set (group->json,
+                                     "age_mask",
+                                     json_integer (meta.age_mask.bits));
+            GNUNET_assert (0 == r);
           }
 
-          /* Create a new array for the denominations in this group */
+          // Create a new array for the denominations in this group
           list = json_array ();
           GNUNET_assert (NULL != list);
           GNUNET_assert (0 ==
@@ -2216,10 +2245,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
                                                
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
         }
 
-        /*
-         * Now that we have found/created the right group, add the denomination
-         * to the list
-         */
+        // Now that we have found/created the right group, add the
+        // denomination to the list
         {
           struct GNUNET_JSON_PackSpec key_spec;
 
@@ -2259,53 +2286,43 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
           GNUNET_assert (NULL != entry);
         }
 
-        /*
-         * Build up the running hash of all denominations in this group
-         * TODO: FIXME-oec: this is cipher and age_restriction dependent?!
-         */
-        GNUNET_CRYPTO_hash_context_read (group->hash_context,
-                                         &dk->h_denom_pub,
-                                         sizeof (struct GNUNET_HashCode));
+        // Build up the running xor of all hashes of the denominations in this
+        // group
+        GNUNET_CRYPTO_hash_xor (&dk->h_denom_pub.hash,
+                                &group->hash_xor,
+                                &group->hash_xor);
 
-        /* Finally, add the denomination to the list of denominations in this
-         * group */
+        // Finally, add the denomination to the list of denominations in this
+        // group
         list = json_object_get (group->json, denoms_key);
         GNUNET_assert (NULL != list);
         GNUNET_assert (true == json_is_array (list));
         GNUNET_assert (0 ==
                        json_array_append_new (list, entry));
       }
-    }
+    } /* loop over heap ends */
 
-    /* Create the JSON-array of grouped denominations */
+    // Create the JSON-array of grouped denominations
     if (0 <
         GNUNET_CONTAINER_multihashmap_size (denominations_by_group))
     {
       struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
-      struct GNUNET_HashCode all_hashcode;
-      struct GNUNET_HashContext *all_hash_ctx;
       struct groupData *group = NULL;
 
-      all_hash_ctx =
-        GNUNET_CRYPTO_hash_context_start ();
-
       iter =
         GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group);
 
       while (GNUNET_OK ==
-             GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL, (const
-                                                                       void **)
-                                                          &group))
+             GNUNET_CONTAINER_multihashmap_iterator_next (iter,
+                                                          NULL,
+                                                          (const
+                                                           void **) &group))
       {
         struct GNUNET_HashCode hc;
 
-        GNUNET_CRYPTO_hash_context_finish (
-          group->hash_context,
-          &hc);
-
-        GNUNET_CRYPTO_hash_context_read (all_hash_ctx,
-                                         &hc,
-                                         sizeof (struct GNUNET_HashCode));
+        GNUNET_CRYPTO_hash_xor (&group->hash_xor,
+                                &grouped_hash_xor,
+                                &grouped_hash_xor);
 
         GNUNET_assert (0 ==
                        json_object_set (
@@ -2325,12 +2342,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
       GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
       GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
 
-      GNUNET_CRYPTO_hash_context_finish (
-        all_hash_ctx,
-        &all_hashcode);
-
-      /* FIXME-oec: TODO:
-       * sign all_hashcode and add the signature to the /keys response */
     }
   }
 
@@ -2341,7 +2352,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
 
     GNUNET_CRYPTO_hash_context_finish (hash_context,
                                        &hc);
-
     if (GNUNET_OK !=
         create_krd (ksh,
                     &hc,
@@ -2349,7 +2359,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
                     sctx.signkeys,
                     recoup,
                     denoms,
-                    grouped_denominations))
+                    grouped_denominations,
+                    &grouped_hash_xor))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                   "Failed to generate key response data for %s\n",
diff --git a/src/extensions/extension_age_restriction.c 
b/src/extensions/extension_age_restriction.c
index e55c8520..9acc5dcb 100644
--- a/src/extensions/extension_age_restriction.c
+++ b/src/extensions/extension_age_restriction.c
@@ -146,17 +146,17 @@ TALER_age_mask_to_string (
  */
 void
 age_restriction_disable (
-  struct TALER_Extension *this)
+  struct TALER_Extension *ext)
 {
-  if (NULL == this)
+  if (NULL == ext)
     return;
 
-  this->config = NULL;
+  ext->config = NULL;
 
-  if (NULL != this->config_json)
+  if (NULL != ext->config_json)
   {
-    json_decref (this->config_json);
-    this->config_json = NULL;
+    json_decref (ext->config_json);
+    ext->config_json = NULL;
   }
 
   TE_age_restriction_config.mask.bits = 0;
@@ -174,7 +174,7 @@ age_restriction_disable (
  */
 static enum GNUNET_GenericReturnValue
 age_restriction_load_taler_config (
-  struct TALER_Extension *this,
+  struct TALER_Extension *ext,
   const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   char *groups = NULL;
@@ -192,8 +192,8 @@ age_restriction_load_taler_config (
                                              "ENABLED")))
   {
     /* Age restriction is not enabled */
-    this->config = NULL;
-    this->config_json = NULL;
+    ext->config = NULL;
+    ext->config_json = NULL;
     return GNUNET_OK;
   }
 
@@ -228,10 +228,10 @@ age_restriction_load_taler_config (
                 __builtin_popcount (mask.bits) - 1);
     TE_age_restriction_config.mask.bits = mask.bits;
     TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; 
/* no underflow, first bit always set */
-    this->config = &TE_age_restriction_config;
+    ext->config = &TE_age_restriction_config;
 
     /* Note: we do now have TE_age_restriction_config set, however
-     * this->config_json is NOT set, i.e. the extension is not yet active! For
+     * ext->config_json is NOT set, i.e. the extension is not yet active! For
      * age restriction to become active, load_json_config must have been
      * called. */
   }
@@ -244,12 +244,12 @@ age_restriction_load_taler_config (
 
 /**
  * @brief implements the TALER_Extension.load_json_config interface.
- * @param this if NULL, only tests the configuration
+ * @param ext if NULL, only tests the configuration
  * @param config the configuration as json
  */
 static enum GNUNET_GenericReturnValue
 age_restriction_load_json_config (
-  struct TALER_Extension *this,
+  struct TALER_Extension *ext,
   json_t *jconfig)
 {
   struct TALER_AgeMask mask = {0};
@@ -260,10 +260,10 @@ age_restriction_load_json_config (
     return ret;
 
   /* only testing the parser */
-  if (this == NULL)
+  if (ext == NULL)
     return GNUNET_OK;
 
-  if (TALER_Extension_AgeRestriction != this->type)
+  if (TALER_Extension_AgeRestriction != ext->type)
     return GNUNET_SYSERR;
 
   TE_age_restriction_config.mask.bits = mask.bits;
@@ -278,12 +278,12 @@ age_restriction_load_json_config (
     TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1;
   }
 
-  this->config = &TE_age_restriction_config;
+  ext->config = &TE_age_restriction_config;
 
-  if (NULL != this->config_json)
-    json_decref (this->config_json);
+  if (NULL != ext->config_json)
+    json_decref (ext->config_json);
 
-  this->config_json = jconfig;
+  ext->config_json = jconfig;
 
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
               "loaded new age restriction config with age groups: %s\n",
@@ -295,22 +295,22 @@ age_restriction_load_json_config (
 
 /**
  * @brief implements the TALER_Extension.load_json_config interface.
- * @param this if NULL, only tests the configuration
+ * @param ext if NULL, only tests the configuration
  * @param config the configuration as json
  */
 json_t *
 age_restriction_config_to_json (
-  const struct TALER_Extension *this)
+  const struct TALER_Extension *ext)
 {
   char *mask_str;
   json_t *conf;
 
-  GNUNET_assert (NULL != this);
-  GNUNET_assert (NULL != this->config);
+  GNUNET_assert (NULL != ext);
+  GNUNET_assert (NULL != ext->config);
 
-  if (NULL != this->config_json)
+  if (NULL != ext->config_json)
   {
-    return json_copy (this->config_json);
+    return json_copy (ext->config_json);
   }
 
   mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask);
@@ -319,8 +319,8 @@ age_restriction_config_to_json (
     );
 
   return GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_bool ("critical", this->critical),
-    GNUNET_JSON_pack_string ("version", this->version),
+    GNUNET_JSON_pack_bool ("critical", ext->critical),
+    GNUNET_JSON_pack_string ("version", ext->version),
     GNUNET_JSON_pack_object_steal ("config", conf)
     );
 }
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index a8ad6f24..f0b105e9 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -359,6 +359,36 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
   TALER_JSON_pack_amount ("account_fee", &(gfs)->account),     \
   TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse)
 
+/**
+ * Group of Denominations.  These are the common fields of an array of
+ * denominations.
+ *
+ * The corresponding JSON-blob will also contain an array of particular
+ * denominations with only the timestamps, cipher-specific public key and the
+ * master signature.
+ *
+ **/
+struct TALER_DenominationGroup
+{
+  /* currency must be set prior to calling TALER_JSON_spec_denomination_group 
*/
+  const char *currency;
+  enum TALER_DenominationCipher cipher;
+  struct TALER_Amount value;
+  struct TALER_DenomFeeSet fees;
+  struct TALER_AgeMask age_mask;
+};
+
+/**
+ * Generate a parser for a group of denominations.
+ * NOTE: group.currency MUST have been set prior to calling this function.
+ *
+ * @param field name of the field, maybe NULL
+ * @param[out] group denomination group information
+ * @return corresponding field spec
+ */
+struct GNUNET_JSON_Specification
+TALER_JSON_spec_denomination_group (const char *field,
+                                    struct TALER_DenominationGroup *group);
 
 /**
  * Generate line in parser specification for denomination public key.
@@ -371,6 +401,20 @@ struct GNUNET_JSON_Specification
 TALER_JSON_spec_denom_pub (const char *field,
                            struct TALER_DenominationPublicKey *pk);
 
+/**
+ * Generate a parser specification for a denomination public key of a given
+ * cipher.
+ *
+ * @param field name of the field
+ * @parm cipher which cipher type to parse for
+ * @param[out] pk key to fill
+ * @return corresponding field spec
+ */
+struct GNUNET_JSON_Specification
+TALER_JSON_spec_denom_pub_cipher (const char *field,
+                                  const enum TALER_DenominationCipher cipher,
+                                  struct TALER_DenominationPublicKey *pk);
+
 
 /**
  * Generate line in parser specification for denomination signature.
diff --git a/src/json/json_helper.c b/src/json/json_helper.c
index 11aeceef..9752bb9f 100644
--- a/src/json/json_helper.c
+++ b/src/json/json_helper.c
@@ -35,11 +35,15 @@
 static enum TALER_DenominationCipher
 string_to_cipher (const char *cipher_s)
 {
-  if (0 == strcasecmp (cipher_s,
-                       "RSA"))
+  if ((0 == strcasecmp (cipher_s,
+                        "RSA")) ||
+      (0 == strcasecmp (cipher_s,
+                        "RSA+age_restricted")))
     return TALER_DENOMINATION_RSA;
-  if (0 == strcasecmp (cipher_s,
-                       "CS"))
+  if ((0 == strcasecmp (cipher_s,
+                        "CS")) ||
+      (0 == strcasecmp (cipher_s,
+                        "CS+age_restricted")))
     return TALER_DENOMINATION_CS;
   return TALER_DENOMINATION_INVALID;
 }
@@ -239,6 +243,84 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
 }
 
 
+static enum GNUNET_GenericReturnValue
+parse_denomination_group (void *cls,
+                          json_t *root,
+                          struct GNUNET_JSON_Specification *spec)
+{
+  struct TALER_DenominationGroup *group = spec->ptr;
+  const char *cipher;
+  bool age_mask_missing = false;
+  bool has_age_restricted_suffix = false;
+  struct GNUNET_JSON_Specification gspec[] = {
+    GNUNET_JSON_spec_string ("cipher",
+                             &cipher),
+    TALER_JSON_spec_amount ("value",
+                            group->currency,
+                            &group->value),
+    TALER_JSON_SPEC_DENOM_FEES ("fee",
+                                group->currency,
+                                &group->fees),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_uint32 ("age_mask",
+                               &group->age_mask.bits),
+      &age_mask_missing),
+    GNUNET_JSON_spec_end ()
+  };
+  const char *emsg;
+  unsigned int eline;
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (root,
+                         gspec,
+                         &emsg,
+                         &eline))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  group->cipher = string_to_cipher (cipher);
+  if (TALER_DENOMINATION_INVALID == group->cipher)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* age_mask and suffix must be consistent */
+  has_age_restricted_suffix =
+    (NULL != strstr (cipher, "+age_restricted"));
+  if (has_age_restricted_suffix && age_mask_missing)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (age_mask_missing)
+    group->age_mask.bits = 0;
+
+  return GNUNET_OK;
+}
+
+
+struct GNUNET_JSON_Specification
+TALER_JSON_spec_denomination_group (const char *name,
+                                    struct TALER_DenominationGroup *group)
+{
+  struct GNUNET_JSON_Specification ret = {
+    .parser = &parse_denomination_group,
+    .cleaner = NULL,
+    .field = name,
+    .ptr = group,
+    .ptr_size = sizeof(*group),
+    .size_ptr = NULL,
+  };
+
+
+  return ret;
+}
+
+
 /**
  * Parse given JSON object to an encrypted contract.
  *
@@ -330,11 +412,14 @@ parse_denom_pub (void *cls,
 {
   struct TALER_DenominationPublicKey *denom_pub = spec->ptr;
   const char *cipher;
+  bool age_mask_missing = false;
   struct GNUNET_JSON_Specification dspec[] = {
     GNUNET_JSON_spec_string ("cipher",
                              &cipher),
-    GNUNET_JSON_spec_uint32 ("age_mask",
-                             &denom_pub->age_mask.bits),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_uint32 ("age_mask",
+                               &denom_pub->age_mask.bits),
+      &age_mask_missing),
     GNUNET_JSON_spec_end ()
   };
   const char *emsg;
@@ -350,6 +435,10 @@ parse_denom_pub (void *cls,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
+
+  if (age_mask_missing)
+    denom_pub->age_mask.bits = 0;
+
   denom_pub->cipher = string_to_cipher (cipher);
   switch (denom_pub->cipher)
   {
@@ -433,6 +522,93 @@ TALER_JSON_spec_denom_pub (const char *field,
 }
 
 
+/**
+ * Parse given JSON object partially into a denomination public key.
+ *
+ * Depending on the cipher in cls, it parses the corresponding public key type.
+ *
+ * @param cls closure, enum TALER_DenominationCipher
+ * @param cipher cipher to parse for
+ * @param root the json object representing data
+ * @param[out] spec where to write the data
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+static enum GNUNET_GenericReturnValue
+parse_denom_pub_cipher (void *cls,
+                        json_t *root,
+                        struct GNUNET_JSON_Specification *spec)
+{
+  struct TALER_DenominationPublicKey *denom_pub = spec->ptr;
+  enum TALER_DenominationCipher cipher = (enum TALER_DenominationCipher) cls;
+  const char *emsg;
+  unsigned int eline;
+
+  switch (cipher)
+  {
+  case TALER_DENOMINATION_RSA:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_rsa_public_key (
+          "rsa_pub",
+          &denom_pub->details.rsa_public_key),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+  case TALER_DENOMINATION_CS:
+    {
+      struct GNUNET_JSON_Specification ispec[] = {
+        GNUNET_JSON_spec_fixed ("cs_pub",
+                                &denom_pub->details.cs_public_key,
+                                sizeof (denom_pub->details.cs_public_key)),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (root,
+                             ispec,
+                             &emsg,
+                             &eline))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
+    }
+  default:
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+struct GNUNET_JSON_Specification
+TALER_JSON_spec_denom_pub_cipher (const char *field,
+                                  enum TALER_DenominationCipher cipher,
+                                  struct TALER_DenominationPublicKey *pk)
+{
+  struct GNUNET_JSON_Specification ret = {
+    .parser = &parse_denom_pub_cipher,
+    .cleaner = &clean_denom_pub,
+    .field = field,
+    .cls = (void *) cipher,
+    .ptr = pk
+  };
+
+  return ret;
+}
+
+
 /**
  * Parse given JSON object to denomination signature.
  *
diff --git a/src/json/json_pack.c b/src/json/json_pack.c
index 090a8b96..bb52eeb0 100644
--- a/src/json/json_pack.c
+++ b/src/json/json_pack.c
@@ -79,35 +79,38 @@ TALER_JSON_pack_denom_pub (
   struct GNUNET_JSON_PackSpec ps = {
     .field_name = name,
   };
+  struct GNUNET_JSON_PackSpec mask_or_end;
 
   if (NULL == pk)
     return ps;
+
+  mask_or_end = (0 != pk->age_mask.bits) ?
+                GNUNET_JSON_pack_uint64 ("age_mask", pk->age_mask.bits) :
+                GNUNET_JSON_pack_end_ ();
+
   switch (pk->cipher)
   {
   case TALER_DENOMINATION_RSA:
     ps.object
       = GNUNET_JSON_PACK (
-          GNUNET_JSON_pack_string ("cipher",
-                                   "RSA"),
-          GNUNET_JSON_pack_uint64 ("age_mask",
-                                   pk->age_mask.bits),
+          GNUNET_JSON_pack_string ("cipher", "RSA"),
           GNUNET_JSON_pack_rsa_public_key ("rsa_public_key",
-                                           pk->details.rsa_public_key));
+                                           pk->details.rsa_public_key),
+          mask_or_end);
     break;
   case TALER_DENOMINATION_CS:
     ps.object
       = GNUNET_JSON_PACK (
-          GNUNET_JSON_pack_string ("cipher",
-                                   "CS"),
-          GNUNET_JSON_pack_uint64 ("age_mask",
-                                   pk->age_mask.bits),
+          GNUNET_JSON_pack_string ("cipher", "CS"),
           GNUNET_JSON_pack_data_varsize ("cs_public_key",
                                          &pk->details.cs_public_key,
-                                         sizeof (pk->details.cs_public_key)));
+                                         sizeof (pk->details.cs_public_key)),
+          mask_or_end);
     break;
   default:
     GNUNET_assert (0);
   }
+
   return ps;
 }
 
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 0e76b289..be7bb3c3 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -303,24 +303,31 @@ parse_json_signkey (struct 
TALER_EXCHANGE_SigningPublicKey *sign_key,
 
 
 /**
- * Parse a exchange's denomination key encoded in JSON.
+ * Parse a exchange's denomination key encoded in JSON partially.
+ *
+ * Only the values for master_sig, timestamps and the cipher-specific public
+ * key are parsed.  All other fields (fees, age_mask, value) MUST have been set
+ * prior to calling this function, otherwise the signature verification
+ * performed within this function will fail.
  *
  * @param currency expected currency of all fees
  * @param[out] denom_key where to return the result
+ * @param cipher cipher type to parse
  * @param check_sigs should we check signatures?
  * @param[in] denom_key_obj json to parse
  * @param master_key master key to use to verify signature
- * @param hash_context where to accumulate data for signature verification
+ * @param hash_xor where to accumulate data for signature verification via XOR
  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
  *        invalid or the json malformed.
  */
 static enum GNUNET_GenericReturnValue
-parse_json_denomkey (const char *currency,
-                     struct TALER_EXCHANGE_DenomPublicKey *denom_key,
-                     bool check_sigs,
-                     json_t *denom_key_obj,
-                     struct TALER_MasterPublicKeyP *master_key,
-                     struct GNUNET_HashContext *hash_context)
+parse_json_denomkey_partially (const char *currency,
+                               struct TALER_EXCHANGE_DenomPublicKey *denom_key,
+                               enum TALER_DenominationCipher cipher,
+                               bool check_sigs,
+                               json_t *denom_key_obj,
+                               struct TALER_MasterPublicKeyP *master_key,
+                               struct GNUNET_HashCode *hash_xor)
 {
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("master_sig",
@@ -333,14 +340,9 @@ parse_json_denomkey (const char *currency,
                                 &denom_key->valid_from),
     GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
                                 &denom_key->expire_legal),
-    TALER_JSON_spec_amount ("value",
-                            currency,
-                            &denom_key->value),
-    TALER_JSON_SPEC_DENOM_FEES ("fee",
-                                currency,
-                                &denom_key->fees),
-    TALER_JSON_spec_denom_pub ("denom_pub",
-                               &denom_key->key),
+    TALER_JSON_spec_denom_pub_cipher (NULL,
+                                      cipher,
+                                      &denom_key->key),
     GNUNET_JSON_spec_end ()
   };
 
@@ -354,10 +356,11 @@ parse_json_denomkey (const char *currency,
   }
   TALER_denom_pub_hash (&denom_key->key,
                         &denom_key->h_key);
-  if (NULL != hash_context)
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &denom_key->h_key,
-                                     sizeof (struct GNUNET_HashCode));
+  if (NULL != hash_xor)
+    GNUNET_CRYPTO_hash_xor (&denom_key->h_key.hash,
+                            hash_xor,
+                            hash_xor);
+
   if (! check_sigs)
     return GNUNET_OK;
   EXITIF (GNUNET_SYSERR ==
@@ -729,15 +732,13 @@ decode_keys_json (const json_t *resp_obj,
                   struct TALER_EXCHANGE_Keys *key_data,
                   enum TALER_EXCHANGE_VersionCompatibility *vc)
 {
-  struct TALER_ExchangeSignatureP sig;
-  struct GNUNET_HashContext *hash_context = NULL;
-  struct GNUNET_HashContext *hash_context_restricted = NULL;
-  bool have_age_restricted_denom = false;
+  struct TALER_ExchangeSignatureP denominations_sig;
+  struct GNUNET_HashCode hash_xor = {0};
   struct TALER_ExchangePublicKeyP pub;
   const char *currency;
   struct GNUNET_JSON_Specification mspec[] = {
-    GNUNET_JSON_spec_fixed_auto ("eddsa_sig",
-                                 &sig),
+    GNUNET_JSON_spec_fixed_auto ("denominations_sig",
+                                 &denominations_sig),
     GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
                                  &pub),
     GNUNET_JSON_spec_fixed_auto ("master_public_key",
@@ -760,7 +761,7 @@ decode_keys_json (const json_t *resp_obj,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-#if DEBUG
+#if 1 /* DEBUG */
   json_dumpf (resp_obj,
               stderr,
               JSON_INDENT (2));
@@ -829,13 +830,6 @@ decode_keys_json (const json_t *resp_obj,
     }
   }
 
-  /* parse the master public key and issue date of the response */
-  if (check_sig)
-  {
-    hash_context = GNUNET_CRYPTO_hash_context_start ();
-    hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
-  }
-
   /* parse the global fees */
   {
     json_t *global_fees;
@@ -933,93 +927,101 @@ decode_keys_json (const json_t *resp_obj,
   /* parse the denomination keys, merging with the
      possibly EXISTING array as required (/keys cherry picking) */
   {
-    /* The denominations can be in "denoms" and (optionally) in
-     * "age_restricted_denoms"
-     */
-    struct
-    {
-      char *name;
-      struct GNUNET_HashContext *hc;
-      bool is_optional_age_restriction;
-    }
-    hive[2] = {
-      {
-        "denoms",
-        hash_context,
-        false
-      },
-      {
-        "age_restricted_denoms",
-        hash_context_restricted,
-        true
-      }
-    };
-
-    for (size_t s = 0; s < sizeof(hive) / sizeof(hive[0]); s++)
-    {
-      json_t *denom_keys_array;
-      json_t *denom_key_obj;
-      unsigned int index;
+    json_t *denominations_by_group;
+    json_t *group_obj;
+    unsigned int group_idx;
 
-      denom_keys_array = json_object_get (resp_obj,
-                                          hive[s].name);
+    denominations_by_group =
+      json_object_get (
+        resp_obj,
+        "denominations");
 
-      if (NULL == denom_keys_array)
-        continue;
+    EXITIF (JSON_ARRAY !=
+            json_typeof (denominations_by_group));
 
-      EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
+    json_array_foreach (denominations_by_group, group_idx, group_obj) {
+      /* First, parse { cipher, fees, value, age_mask } of the current group */
 
-      json_array_foreach (denom_keys_array, index, denom_key_obj) {
-        struct TALER_EXCHANGE_DenomPublicKey dk;
-        bool found = false;
+      struct TALER_DenominationGroup group = {
+        .currency = currency
+      };
+      struct GNUNET_JSON_Specification group_spec[] = {
+        TALER_JSON_spec_denomination_group (NULL, &group),
+        GNUNET_JSON_spec_end ()
+      };
 
-        /* mark that we have at least one age restricted denomination, needed
-         * for the hash calculation and signature verification below. */
-        have_age_restricted_denom |= hive[s].is_optional_age_restriction;
+      EXITIF (GNUNET_SYSERR ==
+              GNUNET_JSON_parse (group_obj,
+                                 group_spec,
+                                 NULL,
+                                 NULL));
 
-        memset (&dk,
-                0,
-                sizeof (dk));
-        EXITIF (GNUNET_SYSERR ==
-                parse_json_denomkey (key_data->currency,
-                                     &dk,
-                                     check_sig,
-                                     denom_key_obj,
-                                     &key_data->master_pub,
-                                     hive[s].hc));
+      /* Now, parse the individual denominations */
+      {
+        json_t *denom_keys_array;
+        json_t *denom_key_obj;
+        unsigned int index;
+        denom_keys_array = json_object_get (group_obj, "denoms");
+        EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
+
+        json_array_foreach (denom_keys_array, index, denom_key_obj) {
+          struct TALER_EXCHANGE_DenomPublicKey dk = {0};
+          bool found = false;
+
+          memset (&dk, 0, sizeof (dk));
+
+          /* Set the common fields from the group for this particular
+           * denomination.  Required to make the validity check inside
+           * parse_json_denomkey_partially pass */
+          dk.key.cipher = group.cipher;
+          dk.value = group.value;
+          dk.fees = group.fees;
+          dk.key.age_mask = group.age_mask;
+
+          EXITIF (GNUNET_SYSERR ==
+                  parse_json_denomkey_partially (key_data->currency,
+                                                 &dk,
+                                                 group.cipher,
+                                                 check_sig,
+                                                 denom_key_obj,
+                                                 &key_data->master_pub,
+                                                 check_sig ? &hash_xor: NULL));
+
+          for (unsigned int j = 0;
+               j<key_data->num_denom_keys;
+               j++)
+          {
+            if (0 == denoms_cmp (&dk,
+                                 &key_data->denom_keys[j]))
+            {
+              found = true;
+              break;
+            }
+          }
 
-        for (unsigned int j = 0;
-             j<key_data->num_denom_keys;
-             j++)
-        {
-          if (0 == denoms_cmp (&dk,
-                               &key_data->denom_keys[j]))
+          if (found)
           {
-            found = true;
-            break;
+            /* 0:0:0 did not support /keys cherry picking */
+            TALER_LOG_DEBUG ("Skipping denomination key: already know it\n");
+            TALER_denom_pub_free (&dk.key);
+            continue;
           }
+
+          if (key_data->denom_keys_size == key_data->num_denom_keys)
+            GNUNET_array_grow (key_data->denom_keys,
+                               key_data->denom_keys_size,
+                               key_data->denom_keys_size * 2 + 2);
+          key_data->denom_keys[key_data->num_denom_keys++] = dk;
+
+          /* Update "last_denom_issue_date" */
+          TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n",
+                           GNUNET_TIME_timestamp2s (dk.valid_from));
+          key_data->last_denom_issue_date
+            = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date,
+                                         dk.valid_from);
         }
-        if (found)
-        {
-          /* 0:0:0 did not support /keys cherry picking */
-          TALER_LOG_DEBUG ("Skipping denomination key: already know it\n");
-          TALER_denom_pub_free (&dk.key);
-          continue;
-        }
-        if (key_data->denom_keys_size == key_data->num_denom_keys)
-          GNUNET_array_grow (key_data->denom_keys,
-                             key_data->denom_keys_size,
-                             key_data->denom_keys_size * 2 + 2);
-        key_data->denom_keys[key_data->num_denom_keys++] = dk;
-
-        /* Update "last_denom_issue_date" */
-        TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n",
-                         GNUNET_TIME_timestamp2s (dk.valid_from));
-        key_data->last_denom_issue_date
-          = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date,
-                                       dk.valid_from);
       };
-    }
+    };
   }
 
   /* parse the auditor information */
@@ -1139,30 +1141,6 @@ decode_keys_json (const json_t *resp_obj,
 
   if (check_sig)
   {
-    struct GNUNET_HashCode hc;
-
-    /* If we had any age restricted denominations, add their hash to the end of
-     * the normal denominations. */
-    if (have_age_restricted_denom)
-    {
-      struct GNUNET_HashCode hcr;
-
-      GNUNET_CRYPTO_hash_context_finish (hash_context_restricted,
-                                         &hcr);
-      hash_context_restricted = NULL;
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       &hcr,
-                                       sizeof(struct GNUNET_HashCode));
-    }
-    else
-    {
-      GNUNET_CRYPTO_hash_context_abort (hash_context_restricted);
-      hash_context_restricted = NULL;
-    }
-
-    GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                       &hc);
-    hash_context = NULL;
     EXITIF (GNUNET_OK !=
             TALER_EXCHANGE_test_signing_key (key_data,
                                              &pub));
@@ -1170,18 +1148,15 @@ decode_keys_json (const json_t *resp_obj,
     EXITIF (GNUNET_OK !=
             TALER_exchange_online_key_set_verify (
               key_data->list_issue_date,
-              &hc,
+              &hash_xor,
               &pub,
-              &sig));
+              &denominations_sig));
   }
+
   return GNUNET_OK;
-EXITIF_exit:
 
+EXITIF_exit:
   *vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
-  if (NULL != hash_context)
-    GNUNET_CRYPTO_hash_context_abort (hash_context);
-  if (NULL != hash_context_restricted)
-    GNUNET_CRYPTO_hash_context_abort (hash_context_restricted);
   return GNUNET_SYSERR;
 }
 

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