gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 03/13: [new /keys response] create and parse denominati


From: gnunet
Subject: [taler-exchange] 03/13: [new /keys response] create and parse denomination implemented
Date: Sun, 26 Jun 2022 17:15:04 +0200

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

oec pushed a commit to branch master
in repository exchange.

commit 31f74059e0d710254397688aabc201b230ef27da
Author: Özgür Kesim <oec-taler@kesim.org>
AuthorDate: Sun Jun 26 16:59:27 2022 +0200

    [new /keys response] create and parse denomination implemented
    
    - /keys response now contains signed denomintations
            - hashes of denominations now XOR'ed per group into a single 
hash-code
            - final hash-code is now XOR of all group hash codes
            - final hash-code is signed
    
    - lib/exchange_api_handle support for new "denominations" implemented
            - parses array of denomation groups
            - creates running xor of hashes
            - verifies signature at the end
            - previous diff/merge logic for keys remains intact.
---
 src/exchange/taler-exchange-httpd_keys.c | 119 ++++++++------
 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 ++++++++++++++-----------------
 5 files changed, 424 insertions(+), 207 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index 7580a8d7..de5f1fbc 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -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 */
@@ -1998,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);
@@ -2043,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 */
@@ -2065,6 +2104,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
         GNUNET_CRYPTO_hash_context_finish (
           GNUNET_CRYPTO_hash_context_copy (hash_context),
           &hc);
+
         if (GNUNET_OK !=
             create_krd (ksh,
                         &hc,
@@ -2072,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",
@@ -2139,21 +2181,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
         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_Amount value;
-          struct TALER_DenomFeeSet fees;
-          struct TALER_AgeMask age_mask;
-        } 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 =
@@ -2168,15 +2203,15 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
           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);
@@ -2190,10 +2225,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
 
           if (age_restricted)
           {
-            char *mask = TALER_age_mask_to_string (&meta.age_mask);
             int r = json_object_set (group->json,
                                      "age_mask",
-                                     json_string (mask));
+                                     json_integer (meta.age_mask.bits));
             GNUNET_assert (0 == r);
           }
 
@@ -2252,12 +2286,11 @@ 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 dependend?!
-        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
@@ -2267,37 +2300,29 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
         GNUNET_assert (0 ==
                        json_array_append_new (list, entry));
       }
-    }
+    } /* loop over heap ends */
 
     // 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 (
@@ -2317,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
     }
   }
 
@@ -2333,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,
@@ -2341,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/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]