gnunet-svn
[Top][All Lists]
Advanced

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

[taler-donau] branch master updated: [lib] restore donau_api_handle


From: gnunet
Subject: [taler-donau] branch master updated: [lib] restore donau_api_handle
Date: Fri, 05 Jan 2024 15:19:14 +0100

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

lukas-matyja pushed a commit to branch master
in repository donau.

The following commit(s) were added to refs/heads/master by this push:
     new 15e70b7  [lib] restore donau_api_handle
15e70b7 is described below

commit 15e70b7ccc67b831200537db0ede96b78774a107
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
AuthorDate: Fri Jan 5 15:19:09 2024 +0100

    [lib] restore donau_api_handle
---
 src/donau/donau-httpd_keys.c |   10 +-
 src/lib/donau_api_handle.c   | 1697 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1702 insertions(+), 5 deletions(-)

diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c
index 3268200..e71d646 100644
--- a/src/donau/donau-httpd_keys.c
+++ b/src/donau/donau-httpd_keys.c
@@ -771,11 +771,11 @@ DH_keys_update_states ()
     // .type = htons (TALER_DBEVENT_DONAU_KEYS_UPDATED),
   };
 
-  // DH_plugin->event_notify (DH_plugin->cls,
-  //                          &es,
-  //                          NULL,
-  //                          0);
-  // key_generation++;
+  DH_plugin->event_notify (DH_plugin->cls,
+                           &es,
+                           NULL,
+                           0);
+  key_generation++;
   // DH_resume_keys_requests (false);
 }
 
diff --git a/src/lib/donau_api_handle.c b/src/lib/donau_api_handle.c
index eb755a6..8c720bb 100644
--- a/src/lib/donau_api_handle.c
+++ b/src/lib/donau_api_handle.c
@@ -127,3 +127,1700 @@ struct DONAU_GetKeysHandle
         do {                                                            \
           if (cond) { GNUNET_break (0); goto EXITIF_exit; }             \
         } while (0)
+
+/**
+ * Parse a donau's signing key encoded in JSON.
+ *
+ * @param[out] sign_key where to return the result
+ * @param sign_key_obj json to parse
+ * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
+ *        invalid or the @a sign_key_obj is malformed.
+ */
+static enum GNUNET_GenericReturnValue
+parse_json_signkey (struct DONAU_SigningPublicKeyAndValidity *sign_key,
+                    const json_t *sign_key_obj)
+{
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &sign_key->master_sig),
+    GNUNET_JSON_spec_fixed_auto ("key",
+                                 &sign_key->key),
+    GNUNET_JSON_spec_timestamp ("stamp_start",
+                                &sign_key->valid_from),
+    GNUNET_JSON_spec_timestamp ("stamp_expire",
+                                &sign_key->valid_until),
+    GNUNET_JSON_spec_timestamp ("stamp_end",
+                                &sign_key->valid_legal),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (sign_key_obj,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (! check_sigs)
+    return GNUNET_OK;
+  if (GNUNET_OK !=
+      TALER_donau_offline_signkey_validity_verify (
+        &sign_key->key,
+        sign_key->valid_from,
+        sign_key->valid_until,
+        sign_key->valid_legal,
+        master_key,
+        &sign_key->master_sig))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Parse a donau'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[out] denom_key where to return the result
+ * @param cipher cipher type to parse
+ * @param check_sigs should we check signatures?
+ * @param denom_key_obj json to parse
+ * @param master_key master key to use to verify signature
+ * @param[in,out] 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_partially (
+  struct DONAU_DenomPublicKey *denom_key,
+  enum TALER_DenominationCipher cipher,
+  bool check_sigs,
+  const 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",
+                                 &denom_key->master_sig),
+    GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
+                                &denom_key->expire_deposit),
+    GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
+                                &denom_key->withdraw_valid_until),
+    GNUNET_JSON_spec_timestamp ("stamp_start",
+                                &denom_key->valid_from),
+    GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
+                                &denom_key->expire_legal),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_bool ("lost",
+                             &denom_key->lost),
+      NULL),
+    TALER_JSON_spec_denom_pub_cipher (NULL,
+                                      cipher,
+                                      &denom_key->key),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (denom_key_obj,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  TALER_denom_pub_hash (&denom_key->key,
+                        &denom_key->h_key);
+  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 ==
+          TALER_donau_offline_denom_validity_verify (
+            &denom_key->h_key,
+            denom_key->valid_from,
+            denom_key->withdraw_valid_until,
+            denom_key->expire_deposit,
+            denom_key->expire_legal,
+            &denom_key->value,
+            &denom_key->fees,
+            master_key,
+            &denom_key->master_sig));
+  return GNUNET_OK;
+EXITIF_exit:
+  /* invalidate denom_key, just to be sure */
+  memset (denom_key,
+          0,
+          sizeof (*denom_key));
+  GNUNET_JSON_parse_free (spec);
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Decode the JSON in @a resp_obj from the /keys response
+ * and store the data in the @a key_data.
+ *
+ * @param[in] resp_obj JSON object to parse
+ * @param check_sig true if we should check the signature
+ * @param[out] key_data where to store the results we decoded
+ * @param[out] vc where to store version compatibility data
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ * (malformed JSON)
+ */
+static enum GNUNET_GenericReturnValue
+decode_keys_json (const json_t *resp_obj,
+                  bool check_sig,
+                  struct DONAU_Keys *key_data,
+                  enum DONAU_VersionCompatibility *vc)
+{
+  struct TALER_DonauSignatureP denominations_sig;
+  struct DONAU_DonauPublicKeyP pub;
+  const json_t *sign_keys_array;
+  const json_t *donation_units_by_group;
+  bool no_signature = false;
+
+  if (JSON_OBJECT != json_typeof (resp_obj))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+#if DEBUG
+  json_dumpf (resp_obj,
+              stderr,
+              JSON_INDENT (2));
+#endif
+  /* check the version first */
+  {
+    const char *ver;
+    unsigned int age;
+    unsigned int revision;
+    unsigned int current;
+    char dummy;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_string ("version",
+                               &ver),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (resp_obj,
+                           spec,
+                           NULL, NULL))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    if (3 != sscanf (ver,
+                     "%u:%u:%u%c",
+                     &current,
+                     &revision,
+                     &age,
+                     &dummy))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    *vc = DONAU_VC_MATCH;
+    if (DONAU_PROTOCOL_CURRENT < current)
+    {
+      *vc |= DONAU_VC_NEWER;
+      if (DONAU_PROTOCOL_CURRENT < current - age)
+        *vc |= DONAU_VC_INCOMPATIBLE;
+    }
+    if (DONAU_PROTOCOL_CURRENT > current)
+    {
+      *vc |= DONAU_VC_OLDER;
+      if (DONAU_PROTOCOL_CURRENT - DONAU_PROTOCOL_AGE > current)
+        *vc |= DONAU_VC_INCOMPATIBLE;
+    }
+    key_data->version = GNUNET_strdup (ver);
+  }
+
+  {
+    const char *currency;
+    const char *asset_type;
+    struct GNUNET_JSON_Specification mspec[] = {
+      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",
+        &key_data->master_pub),
+      GNUNET_JSON_spec_array_const ("accounts",
+                                    &accounts),
+      GNUNET_JSON_spec_object_const ("wire_fees",
+                                     &fees),
+      GNUNET_JSON_spec_array_const ("wads",
+                                    &wads),
+      GNUNET_JSON_spec_timestamp (
+        "list_issue_date",
+        &key_data->list_issue_date),
+      GNUNET_JSON_spec_relative_time (
+        "reserve_closing_delay",
+        &key_data->reserve_closing_delay),
+      GNUNET_JSON_spec_string (
+        "currency",
+        &currency),
+      GNUNET_JSON_spec_uint32 (
+        "currency_fraction_digits",
+        &key_data->currency_fraction_digits),
+      GNUNET_JSON_spec_string (
+        "asset_type",
+        &asset_type),
+      GNUNET_JSON_spec_array_const (
+        "global_fees",
+        &global_fees),
+      GNUNET_JSON_spec_array_const (
+        "signkeys",
+        &sign_keys_array),
+      GNUNET_JSON_spec_array_const (
+        "denominations",
+        &denominations_by_group),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_array_const (
+          "recoup",
+          &recoup_array),
+        NULL),
+      GNUNET_JSON_spec_array_const (
+        "auditors",
+        &auditors_array),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_bool (
+          "rewards_allowed",
+          &key_data->rewards_allowed),
+        NULL),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_object_const ("extensions",
+                                       &manifests),
+        &no_extensions),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_fixed_auto (
+          "extensions_sig",
+          &key_data->extensions_sig),
+        &no_signature),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_array_const (
+          "wallet_balance_limit_without_kyc",
+          &wblwk),
+        NULL),
+      GNUNET_JSON_spec_end ()
+    };
+    const char *emsg;
+    unsigned int eline;
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (resp_obj,
+                           (check_sig) ? mspec : &mspec[2],
+                           &emsg,
+                           &eline))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Parsing /keys failed for `%s' (%u)\n",
+                  emsg,
+                  eline);
+      EXITIF (1);
+    }
+    {
+      struct GNUNET_JSON_Specification sspec[] = {
+        TALER_JSON_spec_amount (
+          "stefan_abs",
+          currency,
+          &key_data->stefan_abs),
+        TALER_JSON_spec_amount (
+          "stefan_log",
+          currency,
+          &key_data->stefan_log),
+        TALER_JSON_spec_amount (
+          "stefan_lin",
+          currency,
+          &key_data->stefan_lin),
+        GNUNET_JSON_spec_end ()
+      };
+
+      EXITIF (GNUNET_OK !=
+              GNUNET_JSON_parse (resp_obj,
+                                 sspec,
+                                 NULL, NULL));
+    }
+
+    key_data->currency = GNUNET_strdup (currency);
+    key_data->asset_type = GNUNET_strdup (asset_type);
+    if (! no_extensions)
+      key_data->extensions = json_incref ((json_t *) manifests);
+  }
+
+  /* parse the global fees */
+  key_data->num_global_fees
+    = json_array_size (global_fees);
+  if (0 != key_data->num_global_fees)
+  {
+    json_t *global_fee;
+    unsigned int index;
+
+    key_data->global_fees
+      = GNUNET_new_array (key_data->num_global_fees,
+                          struct DONAU_GlobalFee);
+    json_array_foreach (global_fees, index, global_fee)
+    {
+      EXITIF (GNUNET_SYSERR ==
+              parse_global_fee (&key_data->global_fees[index],
+                                check_sig,
+                                global_fee,
+                                key_data));
+    }
+  }
+
+  /* parse the signing keys */
+  key_data->num_sign_keys
+    = json_array_size (sign_keys_array);
+  if (0 != key_data->num_sign_keys)
+  {
+    json_t *sign_key_obj;
+    unsigned int index;
+
+    key_data->sign_keys
+      = GNUNET_new_array (key_data->num_sign_keys,
+                          struct DONAU_SigningPublicKey);
+    json_array_foreach (sign_keys_array, index, sign_key_obj) {
+      EXITIF (GNUNET_SYSERR ==
+              parse_json_signkey (&key_data->sign_keys[index],
+                                  check_sig,
+                                  sign_key_obj,
+                                  &key_data->master_pub));
+    }
+  }
+
+  /* Parse balance limits */
+  if (NULL != wblwk)
+  {
+    key_data->wblwk_length = json_array_size (wblwk);
+    key_data->wallet_balance_limit_without_kyc
+      = GNUNET_new_array (key_data->wblwk_length,
+                          struct TALER_Amount);
+    for (unsigned int i = 0; i<key_data->wblwk_length; i++)
+    {
+      struct TALER_Amount *a = &key_data->wallet_balance_limit_without_kyc[i];
+      const json_t *aj = json_array_get (wblwk,
+                                         i);
+      struct GNUNET_JSON_Specification spec[] = {
+        TALER_JSON_spec_amount (NULL,
+                                key_data->currency,
+                                a),
+        GNUNET_JSON_spec_end ()
+      };
+
+      EXITIF (GNUNET_OK !=
+              GNUNET_JSON_parse (aj,
+                                 spec,
+                                 NULL, NULL));
+    }
+  }
+
+  /* Parse wire accounts */
+  key_data->fees = parse_fees (&key_data->master_pub,
+                               key_data->currency,
+                               fees,
+                               &key_data->fees_len);
+  EXITIF (NULL == key_data->fees);
+  /* parse accounts */
+  GNUNET_array_grow (key_data->accounts,
+                     key_data->accounts_len,
+                     json_array_size (accounts));
+  EXITIF (GNUNET_OK !=
+          DONAU_parse_accounts (&key_data->master_pub,
+                                accounts,
+                                key_data->accounts_len,
+                                key_data->accounts));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Parsed %u wire accounts from JSON\n",
+              (unsigned int) json_array_size (accounts));
+
+
+  /* Parse the supported extension(s): age-restriction. */
+  /* TODO: maybe lift all this into a FP in TALER_Extension ? */
+  if (! no_extensions)
+  {
+    if (no_signature)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "found extensions without signature\n");
+    }
+    else
+    {
+      /* We have an extensions object. Verify its signature. */
+      EXITIF (GNUNET_OK !=
+              TALER_extensions_verify_manifests_signature (
+                manifests,
+                &key_data->extensions_sig,
+                &key_data->master_pub));
+
+      /* Parse and set the the configuration of the extensions accordingly */
+      EXITIF (GNUNET_OK !=
+              TALER_extensions_load_manifests (manifests));
+    }
+
+    /* Assuming we might have now a new value for age_mask, set it in key_data 
*/
+    key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
+  }
+
+  /*
+   * Parse the denomination keys, merging with the
+   * possibly EXISTING array as required (/keys cherry picking).
+   *
+   * The denominations are grouped by common values of
+   *    {cipher, value, fee, age_mask}.
+   */
+  {
+    json_t *group_obj;
+    unsigned int group_idx;
+
+    json_array_foreach (denominations_by_group, group_idx, group_obj)
+    {
+      /* Running XOR of each SHA512 hash of the denominations' public key in
+         this group.  Used to compare against group.hash after all keys have
+         been parsed. */
+      struct GNUNET_HashCode group_hash_xor = {0};
+
+      /* First, parse { cipher, fees, value, age_mask, hash } of the current
+         group. */
+      struct TALER_DenominationGroup group = {0};
+      const json_t *denom_keys_array;
+      struct GNUNET_JSON_Specification group_spec[] = {
+        TALER_JSON_spec_denomination_group (NULL,
+                                            key_data->currency,
+                                            &group),
+        GNUNET_JSON_spec_array_const ("denoms",
+                                      &denom_keys_array),
+        GNUNET_JSON_spec_end ()
+      };
+      json_t *denom_key_obj;
+      unsigned int index;
+
+      EXITIF (GNUNET_SYSERR ==
+              GNUNET_JSON_parse (group_obj,
+                                 group_spec,
+                                 NULL,
+                                 NULL));
+
+      /* Now, parse the individual denominations */
+      json_array_foreach (denom_keys_array, index, denom_key_obj)
+      {
+        /* Set the common fields from the group for this particular
+           denomination.  Required to make the validity check inside
+           parse_json_denomkey_partially pass */
+        struct DONAU_DenomPublicKey dk = {
+          .key.cipher = group.cipher,
+          .value = group.value,
+          .fees = group.fees,
+          .key.age_mask = group.age_mask
+        };
+        bool found = false;
+
+        EXITIF (GNUNET_SYSERR ==
+                parse_json_denomkey_partially (&dk,
+                                               group.cipher,
+                                               check_sig,
+                                               denom_key_obj,
+                                               &key_data->master_pub,
+                                               check_sig ? &hash_xor : NULL));
+
+        /* Build the running xor of the SHA512-hash of the public keys for the 
group */
+        GNUNET_CRYPTO_hash_xor (&dk.h_key.hash,
+                                &group_hash_xor,
+                                &group_hash_xor);
+        for (unsigned int j = 0;
+             j<key_data->num_denom_keys;
+             j++)
+        {
+          if (0 == GNUNET_CRYPTO_bsign_pub_cmp (&dk,
+                                                &key_data->denom_keys[j]))
+          {
+            found = true;
+            break;
+          }
+        }
+
+        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);
+      };   /* end of json_array_foreach over denominations */
+
+      /* The calculated group_hash_xor must be the same as group.hash from
+         the JSON. */
+      EXITIF (0 !=
+              GNUNET_CRYPTO_hash_cmp (&group_hash_xor,
+                                      &group.hash));
+
+    } /* end of json_array_foreach over groups of denominations */
+  } /* end of scope for group_ojb/group_idx */
+
+  /* parse the auditor information */
+  {
+    json_t *auditor_info;
+    unsigned int index;
+
+    /* Merge with the existing auditor information we have (/keys cherry 
picking) */
+    json_array_foreach (auditors_array, index, auditor_info)
+    {
+      struct DONAU_AuditorInformation ai;
+      bool found = false;
+
+      memset (&ai,
+              0,
+              sizeof (ai));
+      EXITIF (GNUNET_SYSERR ==
+              parse_json_auditor (&ai,
+                                  check_sig,
+                                  auditor_info,
+                                  key_data));
+      for (unsigned int j = 0; j<key_data->num_auditors; j++)
+      {
+        struct DONAU_AuditorInformation *aix = &key_data->auditors[j];
+
+        if (0 == GNUNET_memcmp (&ai.auditor_pub,
+                                &aix->auditor_pub))
+        {
+          found = true;
+          /* Merge denomination key signatures of downloaded /keys into 
existing
+             auditor information 'aix'. */
+          TALER_LOG_DEBUG (
+            "Merging %u new audited keys with %u known audited keys\n",
+            aix->num_denom_keys,
+            ai.num_denom_keys);
+          for (unsigned int i = 0; i<ai.num_denom_keys; i++)
+          {
+            bool kfound = false;
+
+            for (unsigned int k = 0; k<aix->num_denom_keys; k++)
+            {
+              if (aix->denom_keys[k].denom_key_offset ==
+                  ai.denom_keys[i].denom_key_offset)
+              {
+                kfound = true;
+                break;
+              }
+            }
+            if (! kfound)
+              GNUNET_array_append (aix->denom_keys,
+                                   aix->num_denom_keys,
+                                   ai.denom_keys[i]);
+          }
+          break;
+        }
+      }
+      if (found)
+      {
+        GNUNET_array_grow (ai.denom_keys,
+                           ai.num_denom_keys,
+                           0);
+        GNUNET_free (ai.auditor_url);
+        continue; /* we are done */
+      }
+      if (key_data->auditors_size == key_data->num_auditors)
+        GNUNET_array_grow (key_data->auditors,
+                           key_data->auditors_size,
+                           key_data->auditors_size * 2 + 2);
+      GNUNET_assert (NULL != ai.auditor_url);
+      key_data->auditors[key_data->num_auditors++] = ai;
+    };
+  }
+
+  /* parse the revocation/recoup information */
+  if (NULL != recoup_array)
+  {
+    json_t *recoup_info;
+    unsigned int index;
+
+    json_array_foreach (recoup_array, index, recoup_info)
+    {
+      struct TALER_DenominationHashP h_denom_pub;
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                     &h_denom_pub),
+        GNUNET_JSON_spec_end ()
+      };
+
+      EXITIF (GNUNET_OK !=
+              GNUNET_JSON_parse (recoup_info,
+                                 spec,
+                                 NULL, NULL));
+      for (unsigned int j = 0;
+           j<key_data->num_denom_keys;
+           j++)
+      {
+        if (0 == GNUNET_memcmp (&h_denom_pub,
+                                &key_data->denom_keys[j].h_key))
+        {
+          key_data->denom_keys[j].revoked = true;
+          break;
+        }
+      }
+    }
+  }
+
+  if (check_sig)
+  {
+    EXITIF (GNUNET_OK !=
+            DONAU_test_signing_key (key_data,
+                                    &pub));
+    EXITIF (GNUNET_OK !=
+            TALER_donau_online_key_set_verify (
+              key_data->list_issue_date,
+              &hash_xor,
+              &pub,
+              &denominations_sig));
+  }
+  return GNUNET_OK;
+
+EXITIF_exit:
+  *vc = DONAU_VC_PROTOCOL_ERROR;
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Callback used when downloading the reply to a /keys request
+ * is complete.
+ *
+ * @param cls the `struct KeysRequest`
+ * @param response_code HTTP response code, 0 on error
+ * @param resp_obj parsed JSON result, NULL on error
+ */
+static void
+keys_completed_cb (void *cls,
+                   long response_code,
+                   const void *resp_obj)
+{
+  struct DONAU_GetKeysHandle *gkh = cls;
+  const json_t *j = resp_obj;
+  struct DONAU_Keys *kd = NULL;
+  struct DONAU_KeysResponse kresp = {
+    .hr.reply = j,
+    .hr.http_status = (unsigned int) response_code,
+    .details.ok.compat = DONAU_VC_PROTOCOL_ERROR,
+  };
+
+  gkh->job = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received keys from URL `%s' with status %ld and expiration 
%s.\n",
+              gkh->url,
+              response_code,
+              GNUNET_TIME_timestamp2s (gkh->expire));
+  if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Donau failed to give expiration time, assuming in %s\n",
+                GNUNET_TIME_relative2s (DEFAULT_EXPIRATION,
+                                        true));
+    gkh->expire
+      = GNUNET_TIME_absolute_to_timestamp (
+          GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
+  }
+  switch (response_code)
+  {
+  case 0:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to receive /keys response from donau %s\n",
+                gkh->donau_url);
+    break;
+  case MHD_HTTP_OK:
+    if (NULL == j)
+    {
+      GNUNET_break (0);
+      response_code = 0;
+      break;
+    }
+    kd = GNUNET_new (struct DONAU_Keys);
+    kd->donau_url = GNUNET_strdup (gkh->donau_url);
+    if (NULL != gkh->prev_keys)
+    {
+      const struct DONAU_Keys *kd_old = gkh->prev_keys;
+
+      /* We keep the denomination keys and auditor signatures from the
+         previous iteration (/keys cherry picking) */
+      kd->num_denom_keys
+        = kd_old->num_denom_keys;
+      kd->last_denom_issue_date
+        = kd_old->last_denom_issue_date;
+      GNUNET_array_grow (kd->denom_keys,
+                         kd->denom_keys_size,
+                         kd->num_denom_keys);
+      /* First make a shallow copy, we then need another pass for the RSA 
key... */
+      GNUNET_memcpy (kd->denom_keys,
+                     kd_old->denom_keys,
+                     kd_old->num_denom_keys
+                     * sizeof (struct DONAU_DenomPublicKey));
+      for (unsigned int i = 0; i<kd_old->num_denom_keys; i++)
+        TALER_denom_pub_deep_copy (&kd->denom_keys[i].key,
+                                   &kd_old->denom_keys[i].key);
+      kd->num_auditors = kd_old->num_auditors;
+      kd->auditors = GNUNET_new_array (kd->num_auditors,
+                                       struct DONAU_AuditorInformation);
+      /* Now the necessary deep copy... */
+      for (unsigned int i = 0; i<kd_old->num_auditors; i++)
+      {
+        const struct DONAU_AuditorInformation *aold =
+          &kd_old->auditors[i];
+        struct DONAU_AuditorInformation *anew = &kd->auditors[i];
+
+        anew->auditor_pub = aold->auditor_pub;
+        anew->auditor_url = GNUNET_strdup (aold->auditor_url);
+        GNUNET_array_grow (anew->denom_keys,
+                           anew->num_denom_keys,
+                           aold->num_denom_keys);
+        GNUNET_memcpy (
+          anew->denom_keys,
+          aold->denom_keys,
+          aold->num_denom_keys
+          * sizeof (struct DONAU_AuditorDenominationInfo));
+      }
+    }
+    /* Now decode fresh /keys response */
+    if (GNUNET_OK !=
+        decode_keys_json (j,
+                          true,
+                          kd,
+                          &kresp.details.ok.compat))
+    {
+      TALER_LOG_ERROR ("Could not decode /keys response\n");
+      kd->rc = 1;
+      DONAU_keys_decref (kd);
+      kd = NULL;
+      kresp.hr.http_status = 0;
+      kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+      break;
+    }
+    kd->rc = 1;
+    kd->key_data_expiration = gkh->expire;
+    if (GNUNET_TIME_relative_cmp (
+          GNUNET_TIME_absolute_get_remaining (gkh->expire.abs_time),
+          <,
+          MINIMUM_EXPIRATION))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Donau returned keys with expiration time below %s. 
Compensating.\n",
+                  GNUNET_TIME_relative2s (MINIMUM_EXPIRATION,
+                                          true));
+      kd->key_data_expiration
+        = GNUNET_TIME_relative_to_timestamp (MINIMUM_EXPIRATION);
+    }
+
+    kresp.details.ok.keys = kd;
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+  case MHD_HTTP_UNAUTHORIZED:
+  case MHD_HTTP_FORBIDDEN:
+  case MHD_HTTP_NOT_FOUND:
+    if (NULL == j)
+    {
+      kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+      kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
+    }
+    else
+    {
+      kresp.hr.ec = TALER_JSON_get_error_code (j);
+      kresp.hr.hint = TALER_JSON_get_error_hint (j);
+    }
+    break;
+  default:
+    if (NULL == j)
+    {
+      kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+      kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
+    }
+    else
+    {
+      kresp.hr.ec = TALER_JSON_get_error_code (j);
+      kresp.hr.hint = TALER_JSON_get_error_hint (j);
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u/%d\n",
+                (unsigned int) response_code,
+                (int) kresp.hr.ec);
+    break;
+  }
+  gkh->cert_cb (gkh->cert_cb_cls,
+                &kresp,
+                kd);
+  DONAU_get_keys_cancel (gkh);
+}
+
+
+/**
+ * Define a max length for the HTTP "Expire:" header
+ */
+#define MAX_DATE_LINE_LEN 32
+
+
+/**
+ * Parse HTTP timestamp.
+ *
+ * @param dateline header to parse header
+ * @param[out] at where to write the result
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_date_string (const char *dateline,
+                   struct GNUNET_TIME_Timestamp *at)
+{
+  static const char *MONTHS[] =
+  { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
+  int year;
+  int mon;
+  int day;
+  int hour;
+  int min;
+  int sec;
+  char month[4];
+  struct tm tm;
+  time_t t;
+
+  /* We recognize the three formats in RFC2616, section 3.3.1.  Month
+     names are always in English.  The formats are:
+      Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
+      Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+      Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+     Note that the first is preferred.
+   */
+
+  if (strlen (dateline) > MAX_DATE_LINE_LEN)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  while (*dateline == ' ')
+    ++dateline;
+  while (*dateline && *dateline != ' ')
+    ++dateline;
+  while (*dateline == ' ')
+    ++dateline;
+  /* We just skipped over the day of the week. Now we have:*/
+  if ( (sscanf (dateline,
+                "%d %3s %d %d:%d:%d",
+                &day, month, &year, &hour, &min, &sec) != 6) &&
+       (sscanf (dateline,
+                "%d-%3s-%d %d:%d:%d",
+                &day, month, &year, &hour, &min, &sec) != 6) &&
+       (sscanf (dateline,
+                "%3s %d %d:%d:%d %d",
+                month, &day, &hour, &min, &sec, &year) != 6) )
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  /* Two digit dates are defined to be relative to 1900; all other dates
+   * are supposed to be represented as four digits. */
+  if (year < 100)
+    year += 1900;
+
+  for (mon = 0; ; mon++)
+  {
+    if (! MONTHS[mon])
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    if (0 == strcasecmp (month,
+                         MONTHS[mon]))
+      break;
+  }
+
+  memset (&tm, 0, sizeof(tm));
+  tm.tm_year = year - 1900;
+  tm.tm_mon = mon;
+  tm.tm_mday = day;
+  tm.tm_hour = hour;
+  tm.tm_min = min;
+  tm.tm_sec = sec;
+
+  t = mktime (&tm);
+  if (((time_t) -1) == t)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                         "mktime");
+    return GNUNET_SYSERR;
+  }
+  if (t < 0)
+    t = 0; /* can happen due to timezone issues if date was 1.1.1970 */
+  *at = GNUNET_TIME_timestamp_from_s (t);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called for each header in the HTTP /keys response.
+ * Finds the "Expire:" header and parses it, storing the result
+ * in the "expire" field of the keys request.
+ *
+ * @param buffer header data received
+ * @param size size of an item in @a buffer
+ * @param nitems number of items in @a buffer
+ * @param userdata the `struct DONAU_GetKeysHandle`
+ * @return `size * nitems` on success (everything else aborts)
+ */
+static size_t
+header_cb (char *buffer,
+           size_t size,
+           size_t nitems,
+           void *userdata)
+{
+  struct DONAU_GetKeysHandle *kr = userdata;
+  size_t total = size * nitems;
+  char *val;
+
+  if (total < strlen (MHD_HTTP_HEADER_EXPIRES ": "))
+    return total;
+  if (0 != strncasecmp (MHD_HTTP_HEADER_EXPIRES ": ",
+                        buffer,
+                        strlen (MHD_HTTP_HEADER_EXPIRES ": ")))
+    return total;
+  val = GNUNET_strndup (&buffer[strlen (MHD_HTTP_HEADER_EXPIRES ": ")],
+                        total - strlen (MHD_HTTP_HEADER_EXPIRES ": "));
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Found %s header `%s'\n",
+              MHD_HTTP_HEADER_EXPIRES,
+              val);
+  if (GNUNET_OK !=
+      parse_date_string (val,
+                         &kr->expire))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to parse %s-header `%s'\n",
+                MHD_HTTP_HEADER_EXPIRES,
+                val);
+    kr->expire = GNUNET_TIME_UNIT_ZERO_TS;
+  }
+  GNUNET_free (val);
+  return total;
+}
+
+
+struct DONAU_GetKeysHandle *
+DONAU_get_keys (
+  struct GNUNET_CURL_Context *ctx,
+  const char *url,
+  struct DONAU_Keys *last_keys,
+  DONAU_GetKeysCallback cert_cb,
+  void *cert_cb_cls)
+{
+  struct DONAU_GetKeysHandle *gkh;
+  CURL *eh;
+
+  TALER_LOG_DEBUG ("Connecting to the donau (%s)\n",
+                   url);
+  gkh = GNUNET_new (struct DONAU_GetKeysHandle);
+  gkh->donau_url = GNUNET_strdup (url);
+  gkh->cert_cb = cert_cb;
+  gkh->cert_cb_cls = cert_cb_cls;
+  gkh->url = TALER_url_join (url,
+                             "keys");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting keys with URL `%s'.\n",
+              gkh->url);
+  eh = DONAU_curl_easy_get_ (gkh->url);
+  if (NULL == eh)
+  {
+    GNUNET_break (0);
+    GNUNET_free (gkh->donau_url);
+    GNUNET_free (gkh->url);
+    GNUNET_free (gkh);
+    return NULL;
+  }
+  GNUNET_break (CURLE_OK ==
+                curl_easy_setopt (eh,
+                                  CURLOPT_VERBOSE,
+                                  0));
+  GNUNET_break (CURLE_OK ==
+                curl_easy_setopt (eh,
+                                  CURLOPT_TIMEOUT,
+                                  120 /* seconds */));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HEADERFUNCTION,
+                                   &header_cb));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HEADERDATA,
+                                   gkh));
+  gkh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
+                                               eh,
+                                               &keys_completed_cb,
+                                               gkh);
+  return gkh;
+}
+
+
+void
+DONAU_get_keys_cancel (
+  struct DONAU_GetKeysHandle *gkh)
+{
+  if (NULL != gkh->job)
+  {
+    GNUNET_CURL_job_cancel (gkh->job);
+    gkh->job = NULL;
+  }
+  DONAU_keys_decref (gkh->prev_keys);
+  GNUNET_free (gkh->donau_url);
+  GNUNET_free (gkh->url);
+  GNUNET_free (gkh);
+}
+
+
+enum GNUNET_GenericReturnValue
+DONAU_test_signing_key (
+  const struct DONAU_Keys *keys,
+  const struct DONAU_DonauPublicKeyP *pub)
+{
+  struct GNUNET_TIME_Absolute now;
+
+  /* we will check using a tolerance of 1h for the time */
+  now = GNUNET_TIME_absolute_get ();
+  for (unsigned int i = 0; i<keys->num_sign_keys; i++)
+    if ( (GNUNET_TIME_absolute_cmp (
+            keys->sign_keys[i].valid_from.abs_time,
+            <=,
+            GNUNET_TIME_absolute_add (now,
+                                      LIFETIME_TOLERANCE))) &&
+         (GNUNET_TIME_absolute_cmp (
+            keys->sign_keys[i].valid_until.abs_time,
+            >,
+            GNUNET_TIME_absolute_subtract (now,
+                                           LIFETIME_TOLERANCE))) &&
+         (0 == GNUNET_memcmp (pub,
+                              &keys->sign_keys[i].key)) )
+      return GNUNET_OK;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Signing key not valid at time %s\n",
+              GNUNET_TIME_absolute2s (now));
+  return GNUNET_SYSERR;
+}
+
+
+const struct DONAU_DenomPublicKey *
+DONAU_get_denomination_key (
+  const struct DONAU_Keys *keys,
+  const struct TALER_DenominationPublicKey *pk)
+{
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+    if (0 ==
+        TALER_denom_pub_cmp (pk,
+                             &keys->denom_keys[i].key))
+      return &keys->denom_keys[i];
+  return NULL;
+}
+
+
+struct DONAU_DenomPublicKey *
+DONAU_copy_denomination_key (
+  const struct DONAU_DenomPublicKey *key)
+{
+  struct DONAU_DenomPublicKey *copy;
+
+  copy = GNUNET_new (struct DONAU_DenomPublicKey);
+  *copy = *key;
+  TALER_denom_pub_deep_copy (&copy->key,
+                             &key->key);
+  return copy;
+}
+
+
+void
+DONAU_destroy_denomination_key (
+  struct DONAU_DenomPublicKey *key)
+{
+  TALER_denom_pub_free (&key->key);
+  GNUNET_free (key);
+}
+
+
+const struct DONAU_DenomPublicKey *
+DONAU_get_denomination_key_by_hash (
+  const struct DONAU_Keys *keys,
+  const struct TALER_DenominationHashP *hc)
+{
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+    if (0 == GNUNET_memcmp (hc,
+                            &keys->denom_keys[i].h_key))
+      return &keys->denom_keys[i];
+  return NULL;
+}
+
+
+struct DONAU_Keys *
+DONAU_keys_incref (struct DONAU_Keys *keys)
+{
+  GNUNET_assert (keys->rc < UINT_MAX);
+  keys->rc++;
+  return keys;
+}
+
+
+void
+DONAU_keys_decref (struct DONAU_Keys *keys)
+{
+  if (NULL == keys)
+    return;
+  GNUNET_assert (0 < keys->rc);
+  keys->rc--;
+  if (0 != keys->rc)
+    return;
+  GNUNET_array_grow (keys->sign_keys,
+                     keys->num_sign_keys,
+                     0);
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+    TALER_denom_pub_free (&keys->denom_keys[i].key);
+
+  GNUNET_array_grow (keys->denom_keys,
+                     keys->denom_keys_size,
+                     0);
+  for (unsigned int i = 0; i<keys->num_auditors; i++)
+  {
+    GNUNET_array_grow (keys->auditors[i].denom_keys,
+                       keys->auditors[i].num_denom_keys,
+                       0);
+    GNUNET_free (keys->auditors[i].auditor_url);
+  }
+  GNUNET_array_grow (keys->auditors,
+                     keys->auditors_size,
+                     0);
+  DONAU_free_accounts (keys->accounts_len,
+                       keys->accounts);
+  GNUNET_array_grow (keys->accounts,
+                     keys->accounts_len,
+                     0);
+  free_fees (keys->fees,
+             keys->fees_len);
+  json_decref (keys->extensions);
+  GNUNET_free (keys->wallet_balance_limit_without_kyc);
+  GNUNET_free (keys->version);
+  GNUNET_free (keys->currency);
+  GNUNET_free (keys->asset_type);
+  GNUNET_free (keys->global_fees);
+  GNUNET_free (keys->donau_url);
+  GNUNET_free (keys);
+}
+
+
+struct DONAU_Keys *
+DONAU_keys_from_json (const json_t *j)
+{
+  const json_t *jkeys;
+  const char *url;
+  uint32_t version;
+  struct GNUNET_TIME_Timestamp expire
+    = GNUNET_TIME_UNIT_ZERO_TS;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_uint32 ("version",
+                             &version),
+    GNUNET_JSON_spec_object_const ("keys",
+                                   &jkeys),
+    GNUNET_JSON_spec_string ("donau_url",
+                             &url),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_timestamp ("expire",
+                                  &expire),
+      NULL),
+    GNUNET_JSON_spec_end ()
+  };
+  struct DONAU_Keys *keys;
+  enum DONAU_VersionCompatibility compat;
+
+  if (NULL == j)
+    return NULL;
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (j,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  if (0 != version)
+  {
+    return NULL; /* unsupported version */
+  }
+  keys = GNUNET_new (struct DONAU_Keys);
+  if (GNUNET_OK !=
+      decode_keys_json (jkeys,
+                        false,
+                        keys,
+                        &compat))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  keys->rc = 1;
+  keys->key_data_expiration = expire;
+  keys->donau_url = GNUNET_strdup (url);
+  return keys;
+}
+
+
+/**
+ * Data we track per donation unit group.
+ */
+struct GroupData
+{
+  /**
+   * The json blob with the group meta-data and list of donation units
+   */
+  json_t *json;
+
+  /**
+   * Meta data for this group.
+   */
+  struct DONAU_DonationUnitGroup meta;
+};
+
+
+/**
+ * Add donation unit group represented by @a value
+ * to list of donation units in @a cls. Also frees
+ * the @a value.
+ *
+ * @param[in,out] cls a `json_t *` with an array to build
+ * @param key unused
+ * @param value a `struct GroupData *`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static enum GNUNET_GenericReturnValue
+add_grp (void *cls,
+         const struct GNUNET_HashCode *key,
+         void *value)
+{
+  json_t *donation_units_by_group = cls;
+  struct GroupData *gd = value;
+  const char *cipher;
+  json_t *ge;
+
+  (void) key;
+  switch (gd->meta.cipher)
+  {
+  case GNUNET_CRYPTO_BSA_RSA:
+    cipher = age_restricted ? "RSA+age_restricted" : "RSA";
+    break;
+  case GNUNET_CRYPTO_BSA_CS:
+    cipher = age_restricted ? "CS+age_restricted" : "CS";
+    break;
+  default:
+    GNUNET_assert (false);
+  }
+
+  ge = GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_data_auto ("hash",
+                                &gd->meta.hash),
+    GNUNET_JSON_pack_string ("cipher",
+                             cipher),
+    GNUNET_JSON_pack_array_steal ("denoms",
+                                  gd->json),
+    TALER_JSON_pack_amount ("value",
+                            &gd->meta.value));
+  GNUNET_assert (0 ==
+                 json_array_append_new (donation_units_by_group,
+                                        ge));
+  GNUNET_free (gd);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Convert array of account restrictions @a ars to JSON.
+ *
+ * @param ar_len length of @a ars
+ * @param ars account restrictions to convert
+ * @return JSON representation
+ */
+static json_t *
+ar_to_json (unsigned int ar_len,
+            const struct TALER_DONAU_AccountRestriction ars[static ar_len])
+{
+  json_t *rval;
+
+  rval = json_array ();
+  GNUNET_assert (NULL != rval);
+  for (unsigned int i = 0; i<ar_len; i++)
+  {
+    const struct TALER_DONAU_AccountRestriction *ar = &ars[i];
+
+    switch (ar->type)
+    {
+    case TALER_DONAU_AR_INVALID:
+      GNUNET_break (0);
+      json_decref (rval);
+      return NULL;
+    case TALER_DONAU_AR_DENY:
+      GNUNET_assert (
+        0 ==
+        json_array_append_new (
+          rval,
+          GNUNET_JSON_PACK (
+            GNUNET_JSON_pack_string ("type",
+                                     "deny"))));
+      break;
+    case TALER_DONAU_AR_REGEX:
+      GNUNET_assert (
+        0 ==
+        json_array_append_new (
+          rval,
+          GNUNET_JSON_PACK (
+            GNUNET_JSON_pack_string (
+              "type",
+              "regex"),
+            GNUNET_JSON_pack_string (
+              "regex",
+              ar->details.regex.posix_egrep),
+            GNUNET_JSON_pack_string (
+              "human_hint",
+              ar->details.regex.human_hint),
+            GNUNET_JSON_pack_object_incref (
+              "human_hint_i18n",
+              (json_t *) ar->details.regex.human_hint_i18n)
+            )));
+      break;
+    }
+  }
+  return rval;
+}
+
+
+json_t *
+DONAU_keys_to_json (const struct DONAU_Keys *kd)
+{
+  json_t *keys;
+  json_t *signkeys;
+  json_t *donation_units;
+  json_t *accounts;
+
+  now = GNUNET_TIME_timestamp_get ();
+  signkeys = json_array ();
+  GNUNET_assert (NULL != signkeys);
+  for (unsigned int i = 0; i<kd->num_sign_keys; i++)
+  {
+    const struct TALER_DONAU_SigningPublicKey *sk = &kd->sign_keys[i];
+    json_t *signkey;
+
+    signkey = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_data_auto ("key",
+                                  &sk->key),
+      GNUNET_JSON_pack_uint64 ("year",
+                                  &sk->year));
+    GNUNET_assert (NULL != signkey);
+    GNUNET_assert (0 ==
+                   json_array_append_new (signkeys,
+                                          signkey));
+  }
+
+  donation_units_by_group = json_array ();
+  GNUNET_assert (NULL != donation_units_by_group);
+  {
+    struct GNUNET_CONTAINER_MultiHashMap *dbg;
+
+    dbg = GNUNET_CONTAINER_multihashmap_create (128,
+                                                false);
+    for (unsigned int i = 0; i<kd->num_donation_unit_keys; i++)
+    {
+      const struct TALER_DONAU_DenomPublicKey *dk = &kd->denom_keys[i];
+      struct TALER_DenominationGroup meta = {
+        .cipher = dk->key.cipher,
+        .value = dk->value,
+        .year = dk->year
+      };
+      struct GNUNET_HashCode key;
+      struct GroupData *gd;
+      json_t *donation_unit;
+      struct GNUNET_JSON_PackSpec key_spec;
+
+      //TODO: check year
+
+      TALER_donation_unit_group_get_key (&meta,
+                                        &key);
+      gd = GNUNET_CONTAINER_multihashmap_get (dbg,
+                                              &key);
+      if (NULL == gd)
+      {
+        gd = GNUNET_new (struct GroupData);
+        gd->meta = meta;
+        gd->json = json_array ();
+        GNUNET_assert (NULL != gd->json);
+        GNUNET_assert (
+          GNUNET_OK ==
+          GNUNET_CONTAINER_multihashmap_put (dbg,
+                                             &key,
+                                             gd,
+                                             
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+
+      }
+
+      switch (meta.cipher)
+      {
+      case GNUNET_CRYPTO_BSA_RSA:
+        key_spec =
+          GNUNET_JSON_pack_rsa_public_key (
+            "rsa_pub",
+            dk->key.bsign_pub_key->details.rsa_public_key);
+        break;
+      case GNUNET_CRYPTO_BSA_CS:
+        key_spec =
+          GNUNET_JSON_pack_data_varsize (
+            "cs_pub",
+            &dk->key.bsign_pub_key->details.cs_public_key,
+            sizeof (dk->key.bsign_pub_key->details.cs_public_key));
+        break;
+      default:
+        GNUNET_assert (false);
+      }
+      donation_unit = GNUNET_JSON_PACK (
+        GNUNET_JSON_pack_timestamp ("stamp_expire_deposit",
+                                    dk->expire_deposit),
+        GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw",
+                                    dk->withdraw_valid_until),
+        GNUNET_JSON_pack_timestamp ("stamp_start",
+                                    dk->valid_from),
+        GNUNET_JSON_pack_timestamp ("stamp_expire_legal",
+                                    dk->expire_legal),
+        GNUNET_JSON_pack_data_auto ("master_sig",
+                                    &dk->master_sig),
+        key_spec
+        );
+      GNUNET_assert (0 ==
+                     json_array_append_new (gd->json,
+                                            donation_unit));
+    }
+    GNUNET_CONTAINER_multihashmap_iterate (dbg,
+                                           &add_grp,
+                                           donation_unit_by_group);
+    GNUNET_CONTAINER_multihashmap_destroy (dbg);
+  }
+
+  auditors = json_array ();
+  GNUNET_assert (NULL != auditors);
+  for (unsigned int i = 0; i<kd->num_auditors; i++)
+  {
+    const struct TALER_DONAU_AuditorInformation *ai = &kd->auditors[i];
+    json_t *a;
+    json_t *adenoms;
+
+    adenoms = json_array ();
+    GNUNET_assert (NULL != adenoms);
+    for (unsigned int j = 0; j<ai->num_denom_keys; j++)
+    {
+      const struct TALER_DONAU_AuditorDenominationInfo *adi =
+        &ai->denom_keys[j];
+      const struct TALER_DONAU_DenomPublicKey *dk =
+        &kd->denom_keys[adi->denom_key_offset];
+      json_t *k;
+
+      GNUNET_assert (adi->denom_key_offset < kd->num_denom_keys);
+      if (GNUNET_TIME_timestamp_cmp (now,
+                                     >,
+                                     dk->expire_deposit))
+        continue; /* skip auditor signatures for denomination keys that have 
expired */
+      GNUNET_assert (adi->denom_key_offset < kd->num_denom_keys);
+      k = GNUNET_JSON_PACK (
+        GNUNET_JSON_pack_data_auto ("denom_pub_h",
+                                    &dk->h_key),
+        GNUNET_JSON_pack_data_auto ("auditor_sig",
+                                    &adi->auditor_sig));
+      GNUNET_assert (0 ==
+                     json_array_append_new (adenoms,
+                                            k));
+    }
+
+    a = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_data_auto ("auditor_pub",
+                                  &ai->auditor_pub),
+      GNUNET_JSON_pack_string ("auditor_url",
+                               ai->auditor_url),
+      GNUNET_JSON_pack_array_steal ("denomination_keys",
+                                    adenoms));
+    GNUNET_assert (0 ==
+                   json_array_append_new (auditors,
+                                          a));
+  }
+
+  global_fees = json_array ();
+  GNUNET_assert (NULL != global_fees);
+  for (unsigned int i = 0; i<kd->num_global_fees; i++)
+  {
+    const struct TALER_DONAU_GlobalFee *gf
+      = &kd->global_fees[i];
+
+    if (GNUNET_TIME_absolute_is_past (gf->end_date.abs_time))
+      continue;
+    GNUNET_assert (
+      0 ==
+      json_array_append_new (
+        global_fees,
+        GNUNET_JSON_PACK (
+          GNUNET_JSON_pack_timestamp ("start_date",
+                                      gf->start_date),
+          GNUNET_JSON_pack_timestamp ("end_date",
+                                      gf->end_date),
+          TALER_JSON_PACK_GLOBAL_FEES (&gf->fees),
+          GNUNET_JSON_pack_time_rel ("history_expiration",
+                                     gf->history_expiration),
+          GNUNET_JSON_pack_time_rel ("purse_timeout",
+                                     gf->purse_timeout),
+          GNUNET_JSON_pack_uint64 ("purse_account_limit",
+                                   gf->purse_account_limit),
+          GNUNET_JSON_pack_data_auto ("master_sig",
+                                      &gf->master_sig))));
+  }
+
+  accounts = json_array ();
+  GNUNET_assert (NULL != accounts);
+  for (unsigned int i = 0; i<kd->accounts_len; i++)
+  {
+    const struct TALER_DONAU_WireAccount *acc
+      = &kd->accounts[i];
+    json_t *credit_restrictions;
+    json_t *debit_restrictions;
+
+    credit_restrictions
+      = ar_to_json (acc->credit_restrictions_length,
+                    acc->credit_restrictions);
+    GNUNET_assert (NULL != credit_restrictions);
+    debit_restrictions
+      = ar_to_json (acc->debit_restrictions_length,
+                    acc->debit_restrictions);
+    GNUNET_assert (NULL != debit_restrictions);
+    GNUNET_assert (
+      0 ==
+      json_array_append_new (
+        accounts,
+        GNUNET_JSON_PACK (
+          GNUNET_JSON_pack_string ("payto_uri",
+                                   acc->payto_uri),
+          GNUNET_JSON_pack_allow_null (
+            GNUNET_JSON_pack_string ("conversion_url",
+                                     acc->conversion_url)),
+          GNUNET_JSON_pack_array_steal ("debit_restrictions",
+                                        debit_restrictions),
+          GNUNET_JSON_pack_array_steal ("credit_restrictions",
+                                        credit_restrictions),
+          GNUNET_JSON_pack_data_auto ("master_sig",
+                                      &acc->master_sig))));
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Serialized %u/%u wire accounts to JSON\n",
+              (unsigned int) json_array_size (accounts),
+              kd->accounts_len);
+
+  wire_fees = json_object ();
+  GNUNET_assert (NULL != wire_fees);
+  for (unsigned int i = 0; i<kd->fees_len; i++)
+  {
+    const struct TALER_DONAU_WireFeesByMethod *fbw
+      = &kd->fees[i];
+    json_t *wf;
+
+    wf = json_array ();
+    GNUNET_assert (NULL != wf);
+    for (struct TALER_DONAU_WireAggregateFees *p = fbw->fees_head;
+         NULL != p;
+         p = p->next)
+    {
+      GNUNET_assert (
+        0 ==
+        json_array_append_new (
+          wf,
+          GNUNET_JSON_PACK (
+            TALER_JSON_pack_amount ("wire_fee",
+                                    &p->fees.wire),
+            TALER_JSON_pack_amount ("closing_fee",
+                                    &p->fees.closing),
+            GNUNET_JSON_pack_timestamp ("start_date",
+                                        p->start_date),
+            GNUNET_JSON_pack_timestamp ("end_date",
+                                        p->end_date),
+            GNUNET_JSON_pack_data_auto ("sig",
+                                        &p->master_sig))));
+    }
+    GNUNET_assert (0 ==
+                   json_object_set_new (wire_fees,
+                                        fbw->method,
+                                        wf));
+  }
+
+  recoup = json_array ();
+  GNUNET_assert (NULL != recoup);
+  for (unsigned int i = 0; i<kd->num_denom_keys; i++)
+  {
+    const struct TALER_DONAU_DenomPublicKey *dk
+      = &kd->denom_keys[i];
+    if (! dk->revoked)
+      continue;
+    GNUNET_assert (0 ==
+                   json_array_append_new (
+                     recoup,
+                     GNUNET_JSON_PACK (
+                       GNUNET_JSON_pack_data_auto ("h_denom_pub",
+                                                   &dk->h_key))));
+  }
+
+  wblwk = json_array ();
+  GNUNET_assert (NULL != wblwk);
+  for (unsigned int i = 0; i<kd->wblwk_length; i++)
+  {
+    const struct TALER_Amount *a = &kd->wallet_balance_limit_without_kyc[i];
+
+    GNUNET_assert (0 ==
+                   json_array_append_new (
+                     wblwk,
+                     TALER_JSON_from_amount (a)));
+  }
+
+  keys = GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_string ("version",
+                             kd->version),
+    GNUNET_JSON_pack_string ("currency",
+                             kd->currency),
+    GNUNET_JSON_pack_uint64 ("currency_fraction_digits",
+                             kd->currency_fraction_digits),
+    GNUNET_JSON_pack_array_steal ("signkeys",
+                                  signkeys),
+    GNUNET_JSON_pack_array_steal ("donation_units",
+                                  donation_units_by_group)
+    );
+  return GNUNET_JSON_PACK (
+    GNUNET_JSON_pack_uint64 ("version",
+                             DONAU_SERIALIZATION_FORMAT_VERSION),
+    GNUNET_JSON_pack_string ("donau_url",
+                             kd->donau_url),
+    GNUNET_JSON_pack_object_steal ("keys",
+                                   keys));
+}
+
+
+/* end of donau_api_handle.c */
\ No newline at end of file

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