donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

donau-httpd_get-keys.c (45802B)


      1 /*
      2  This file is part of TALER
      3  Copyright (C) 2023-2024 Taler Systems SA
      4 
      5  TALER is free software; you can redistribute it and/or modify it under the
      6  terms of the GNU Affero General Public License as published by the Free Software
      7  Foundation; either version 3, or (at your option) any later version.
      8 
      9  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13  You should have received a copy of the GNU Affero General Public License along with
     14  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 /**
     17  * @file donau-httpd_get-keys.c
     18  * @brief management of our various keys
     19  * @author Christian Grothoff
     20  * @author Özgür Kesim
     21  * @author Pius Loosli
     22  * @author Johannes Casaburi
     23  */
     24 #include <donau_config.h>
     25 #include <taler/taler_json_lib.h>
     26 #include <taler/taler_mhd_lib.h>
     27 #include "donau_json_lib.h"
     28 #include "donau-httpd.h"
     29 #include "donau-httpd_get-keys.h"
     30 #include "donau-httpd_get-config.h"
     31 #include "donaudb_lib.h"
     32 #include "donau-database/insert_donation_unit.h"
     33 #include "donau-database/insert_signing_key.h"
     34 #include "donau-database/iterate_donation_units.h"
     35 #include "donau-database/iterate_active_signing_keys.h"
     36 #include "donau-database/preflight.h"
     37 #include "donau_util.h"
     38 
     39 
     40 /**
     41  * @brief All information about an donau online signing key (which is used to
     42  * sign messages from the donau).
     43  */
     44 struct SigningKey
     45 {
     46 
     47   /**
     48    * The donau's (online signing) public key.
     49    */
     50   struct DONAU_DonauPublicKeyP donau_pub;
     51 
     52   /**
     53    * Meta data about the signing key, such as validity periods.
     54    */
     55   struct DONAUDB_SignkeyMetaData meta;
     56 
     57   /**
     58    * Did we lose the private keys? // NEEDED?
     59    */
     60   bool lost;
     61 };
     62 
     63 
     64 /**
     65  * Snapshot of the (coin and signing) keys (including private keys) of
     66  * the exchange.  There can be multiple instances of this struct, as it is
     67  * reference counted and only destroyed once the last user is done
     68  * with it.  The current instance is acquired using
     69  * #TEH_KS_acquire().  Using this function increases the
     70  * reference count.  The contents of this structure (except for the
     71  * reference counter) should be considered READ-ONLY until it is
     72  * ultimately destroyed (as there can be many concurrent users).
     73  */
     74 struct DH_KeyStateHandle
     75 {
     76 
     77   /**
     78    * For which (global) key_generation was this data structure created?
     79    * Used to check when we are outdated and need to be re-generated.
     80    */
     81   uint64_t key_generation;
     82 
     83   /**
     84    * When did we initiate the key reloading?
     85    */
     86   struct GNUNET_TIME_Timestamp reload_time;
     87 
     88   /**
     89    * When does our online signing key expire and we
     90    * thus need to re-generate this response?
     91    */
     92   struct GNUNET_TIME_Timestamp signature_expires;
     93 
     94   /**
     95    * Response to return if the client supports (deflate) compression.
     96    */
     97   struct MHD_Response *response_compressed;
     98 
     99   /**
    100    * Response to return if the client does not support compression.
    101    */
    102   struct MHD_Response *response_uncompressed;
    103 
    104   /**
    105    * ETag for these responses.
    106    */
    107   char *etag;
    108 
    109 };
    110 
    111 
    112 /**
    113  * RSA security module public key, all zero if not known.
    114  */
    115 static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub;
    116 
    117 /**
    118  * CS security module public key, all zero if not known.
    119  */
    120 static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub;
    121 
    122 /**
    123  * EdDSA security module public key, all zero if not known.
    124  */
    125 static struct TALER_SecurityModulePublicKeyP esign_sm_pub;
    126 
    127 /**
    128  * Counter incremented whenever we have a reason to re-build the keys because
    129  * something external changed.  See #DH_keys_get_state() for uses of this
    130  * variable.
    131  */
    132 static uint64_t key_generation;
    133 
    134 /**
    135  * Handle for the esign/EdDSA helper.
    136  */
    137 static struct TALER_CRYPTO_ExchangeSignHelper *esh;
    138 
    139 /**
    140  * Handle for the donation_unit/RSA helper.
    141  */
    142 static struct TALER_CRYPTO_RsaDenominationHelper *rsadh;
    143 
    144 /**
    145  * Handle for the donation_unit/CS helper.
    146  */
    147 static struct TALER_CRYPTO_CsDenominationHelper *csdh;
    148 
    149 /**
    150  * Map from H(rsa_pub) or H(cs_pub) to `struct DH_DonationUnitKey` entries.
    151  */
    152 static struct GNUNET_CONTAINER_MultiHashMap *du_keys;
    153 
    154 /**
    155  * Map from `struct TALER_ExchangePublicKey` to `struct SigningKey`
    156  * entries.  Based on the fact that a `struct GNUNET_PeerIdentity` is also
    157  * an EdDSA public key.
    158  */
    159 static struct GNUNET_CONTAINER_MultiPeerMap *esign_keys;
    160 
    161 /**
    162  * Stores the latest generation of our key state.
    163  */
    164 static struct DH_KeyStateHandle *key_state;
    165 
    166 
    167 /**
    168  * Add the headers we want to set for every /keys response.
    169  *
    170  * @param cls the key state to use
    171  * @param[in,out] response the response to modify
    172  */
    173 static void
    174 setup_general_response_headers (void *cls,
    175                                 struct MHD_Response *response)
    176 {
    177   struct DH_KeyStateHandle *ksh = cls;
    178   char dat[128];
    179 
    180   TALER_MHD_add_global_headers (response,
    181                                 true);
    182   GNUNET_break (
    183     MHD_YES == MHD_add_response_header (response,
    184                                         MHD_HTTP_HEADER_CONTENT_TYPE,
    185                                         "application/json"));
    186   TALER_MHD_get_date_string (ksh->reload_time.abs_time, dat);
    187   GNUNET_break (
    188     MHD_YES == MHD_add_response_header (response,
    189                                         MHD_HTTP_HEADER_LAST_MODIFIED,
    190                                         dat));
    191   /* Set cache control headers: our response varies depending on these headers */
    192   GNUNET_break (
    193     MHD_YES == MHD_add_response_header (response,
    194                                         MHD_HTTP_HEADER_VARY,
    195                                         MHD_HTTP_HEADER_ACCEPT_ENCODING));
    196   /* Information is always public, revalidate after 1 hour */
    197   GNUNET_break (
    198     MHD_YES == MHD_add_response_header (response,
    199                                         MHD_HTTP_HEADER_CACHE_CONTROL,
    200                                         "public,max-age=3600"));
    201 }
    202 
    203 
    204 /**
    205  * Initialize @a ksh using the given values for @a signkeys,
    206  * and @a denoms.
    207  *
    208  * @param[in,out] ksh key state handle we build @a ksh for
    209  * @param[in,out] signkeys list of sign keys to return
    210  * @param[in,out] donation_units list of grouped denominations to return
    211  * @return #GNUNET_OK on success
    212  */
    213 static enum GNUNET_GenericReturnValue
    214 create_keys_response (struct DH_KeyStateHandle *ksh,
    215                       json_t *signkeys,
    216                       json_t *donation_units)
    217 {
    218   json_t *keys;
    219   char *keys_json;
    220   void *keys_jsonz;
    221   size_t keys_jsonz_size;
    222   int comp;
    223   char etag[sizeof (struct GNUNET_HashCode) * 2];
    224 
    225   GNUNET_assert (NULL != signkeys);
    226   GNUNET_assert (NULL != donation_units);
    227   GNUNET_assert (NULL != DH_currency);
    228   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    229               "Creating /keys response\n");
    230 
    231   keys = GNUNET_JSON_PACK (
    232     GNUNET_JSON_pack_string ("version",
    233                              DONAU_PROTOCOL_VERSION),
    234     GNUNET_JSON_pack_string ("base_url",
    235                              DH_base_url),
    236     GNUNET_JSON_pack_string ("currency",
    237                              DH_currency),
    238     GNUNET_JSON_pack_string ("legal_domain",
    239                              DH_legal_domain),
    240     GNUNET_JSON_pack_array_incref ("signkeys",
    241                                    signkeys),
    242     GNUNET_JSON_pack_array_incref ("donation_units",
    243                                    donation_units));
    244   GNUNET_assert (NULL != keys);
    245 
    246   /* Convert /keys response to UTF8-String */
    247   keys_json = json_dumps (keys,
    248                           JSON_INDENT (2));
    249   json_decref (keys);
    250   GNUNET_assert (NULL != keys_json);
    251 
    252   /* Keep copy for later compression... */
    253   keys_jsonz = GNUNET_strdup (keys_json);
    254   keys_jsonz_size = strlen (keys_json);
    255 
    256   /* hash to compute etag */
    257   {
    258     struct GNUNET_HashCode ehash;
    259     char *end;
    260 
    261     GNUNET_CRYPTO_hash (keys_jsonz,
    262                         keys_jsonz_size,
    263                         &ehash);
    264     end = GNUNET_STRINGS_data_to_string (&ehash,
    265                                          sizeof (ehash),
    266                                          etag,
    267                                          sizeof (etag));
    268     *end = '\0';
    269   }
    270 
    271   /* Create uncompressed response */
    272   ksh->response_uncompressed
    273     = MHD_create_response_from_buffer (keys_jsonz_size,
    274                                        keys_json,
    275                                        MHD_RESPMEM_MUST_FREE);
    276   GNUNET_assert (NULL != ksh->response_uncompressed);
    277   setup_general_response_headers (ksh,
    278                                   ksh->response_uncompressed);
    279   GNUNET_break (MHD_YES ==
    280                 MHD_add_response_header (ksh->response_uncompressed,
    281                                          MHD_HTTP_HEADER_ETAG,
    282                                          etag));
    283   /* Also compute compressed version of /keys response */
    284   comp = TALER_MHD_body_compress (&keys_jsonz,
    285                                   &keys_jsonz_size);
    286   ksh->response_compressed
    287     = MHD_create_response_from_buffer (keys_jsonz_size,
    288                                        keys_jsonz,
    289                                        MHD_RESPMEM_MUST_FREE);
    290   GNUNET_assert (NULL != ksh->response_compressed);
    291   /* If the response is actually compressed, set the
    292      respective header. */
    293   GNUNET_assert ( (MHD_YES != comp) ||
    294                   (MHD_YES ==
    295                    MHD_add_response_header (ksh->response_compressed,
    296                                             MHD_HTTP_HEADER_CONTENT_ENCODING,
    297                                             "deflate")) );
    298   setup_general_response_headers (ksh,
    299                                   ksh->response_compressed);
    300   /* Set cache control headers: our response varies depending on these headers */
    301   GNUNET_break (MHD_YES ==
    302                 MHD_add_response_header (ksh->response_compressed,
    303                                          MHD_HTTP_HEADER_VARY,
    304                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
    305   /* Information is always public, revalidate after 1 day */
    306   GNUNET_break (MHD_YES ==
    307                 MHD_add_response_header (ksh->response_compressed,
    308                                          MHD_HTTP_HEADER_CACHE_CONTROL,
    309                                          "public,max-age=86400"));
    310   GNUNET_break (MHD_YES ==
    311                 MHD_add_response_header (ksh->response_compressed,
    312                                          MHD_HTTP_HEADER_ETAG,
    313                                          etag));
    314   ksh->etag = GNUNET_strdup (etag);
    315   return GNUNET_OK;
    316 }
    317 
    318 
    319 /**
    320  * Closure for #insert_donation_unit_cb and #add_signkey_cb.
    321  */
    322 struct KeysBuilderContext
    323 {
    324 
    325   /**
    326    * Array of donation unit keys.
    327    */
    328   json_t *donation_units;
    329 
    330   /**
    331    * Array of signing keys.
    332    */
    333   json_t *signkeys;
    334 
    335 };
    336 
    337 /**
    338  * Function called for all signing keys, used to build up the
    339  * respective JSON response.
    340  *
    341  * @param cls a `struct KeysBuilderContext *` with the array to append keys to
    342  * @param pid the donau public key (in type disguise)
    343  * @param value a `struct SigningKey`
    344  * @return #GNUNET_OK (continue to iterate)
    345  */
    346 static enum GNUNET_GenericReturnValue
    347 add_sign_key_cb (void *cls,
    348                  const struct GNUNET_PeerIdentity *pid,
    349                  void *value)
    350 {
    351   struct KeysBuilderContext *ctx = cls;
    352   struct SigningKey *sk = value;
    353 
    354   (void) pid;
    355   GNUNET_assert (
    356     0 ==
    357     json_array_append_new (
    358       ctx->signkeys,
    359       GNUNET_JSON_PACK (
    360         GNUNET_JSON_pack_timestamp ("stamp_start",
    361                                     sk->meta.valid_from),
    362         GNUNET_JSON_pack_timestamp ("stamp_expire",
    363                                     sk->meta.expire_sign),
    364         GNUNET_JSON_pack_data_auto ("key",
    365                                     &sk->donau_pub))));
    366   return GNUNET_OK;
    367 }
    368 
    369 
    370 /**
    371  * Function called on all of our current and future donation unit keys
    372  * known to the helper process. Filters out those that are current
    373  * and adds the remaining donation unit keys (with their configuration
    374  * data) to the JSON array.
    375  *
    376  * @param cls the `struct KeysBuilderContext *`
    377  * @param h_du_pub hash of the donation unit public key
    378  * @param value a `struct DH_DonationUnitKey`
    379  * @return #GNUNET_OK (continue to iterate)
    380  */
    381 static enum GNUNET_GenericReturnValue
    382 insert_donation_unit_cb (void *cls,
    383                          const struct GNUNET_HashCode *h_du_pub,
    384                          void *value)
    385 {
    386   struct KeysBuilderContext *kbc = cls;
    387   struct DH_DonationUnitKey *du = value;
    388 
    389   (void) h_du_pub;
    390   GNUNET_assert (
    391     0 == json_array_append_new (
    392       kbc->donation_units,
    393       GNUNET_JSON_PACK (
    394         DONAU_JSON_pack_donation_unit_pub ("donation_unit_pub",
    395                                            &du->donation_unit_pub),
    396         GNUNET_JSON_pack_uint64 ("year",
    397                                  du->validity_year),
    398         GNUNET_JSON_pack_bool ("lost",
    399                                du->lost),
    400         TALER_JSON_pack_amount ("value",
    401                                 &du->value)
    402         )));
    403   return GNUNET_OK;
    404 }
    405 
    406 
    407 /**
    408  * Update the "/keys" responses in @a ksh, computing the detailed replies.
    409  *
    410  * This function is to recompute all (including cherry-picked) responses we
    411  * might want to return, based on the state already in @a ksh.
    412  *
    413  * @param[in,out] ksh state handle to update
    414  * @return #GNUNET_OK on success
    415  */
    416 static enum GNUNET_GenericReturnValue
    417 finish_keys_response (struct DH_KeyStateHandle *ksh)
    418 {
    419   enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
    420   struct KeysBuilderContext kbc;
    421 
    422   kbc.signkeys = json_array ();
    423   GNUNET_assert (NULL != kbc.signkeys);
    424   kbc.donation_units = json_array ();
    425   GNUNET_assert (NULL != kbc.donation_units);
    426   GNUNET_CONTAINER_multipeermap_iterate (esign_keys,
    427                                          &add_sign_key_cb,
    428                                          &kbc);
    429 
    430   if (0 == json_array_size (kbc.signkeys))
    431   {
    432     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    433                 "No online signing keys available. Refusing to generate /keys response.\n");
    434     ret = GNUNET_NO;
    435     goto CLEANUP;
    436   }
    437   GNUNET_CONTAINER_multihashmap_iterate (du_keys,
    438                                          &insert_donation_unit_cb,
    439                                          &kbc);
    440 
    441   if (0 == json_array_size (kbc.donation_units))
    442   {
    443     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    444                 "No donation units available. Refusing to generate /keys response.\n");
    445     ret = GNUNET_NO;
    446     goto CLEANUP;
    447   }
    448 
    449   if (GNUNET_OK !=
    450       create_keys_response (ksh,
    451                             kbc.signkeys,
    452                             kbc.donation_units))
    453   {
    454     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    455                 "Failed to generate key response data\n");
    456     goto CLEANUP;
    457   }
    458 
    459   ret = GNUNET_OK;
    460 
    461 CLEANUP:
    462   if (NULL != kbc.donation_units)
    463     json_decref (kbc.donation_units);
    464   if (NULL != kbc.signkeys)
    465     json_decref (kbc.signkeys);
    466   return ret;
    467 }
    468 
    469 
    470 /**
    471  * Free donation unit key data.
    472  *
    473  * @param cls a `struct DH_KeyStateHandle`, unused
    474  * @param h_du_pub hash of the donation unit public key, unused
    475  * @param value a `struct DH_DonationUnitKey` to free
    476  * @return #GNUNET_OK (continue to iterate)
    477  */
    478 static enum GNUNET_GenericReturnValue
    479 clear_donation_unit_cb (void *cls,
    480                         const struct GNUNET_HashCode *h_du_pub,
    481                         void *value)
    482 {
    483   struct DH_DonationUnitKey *dk = value;
    484 
    485   (void) cls;
    486   (void) h_du_pub;
    487   DONAU_donation_unit_pub_free (&dk->donation_unit_pub);
    488   GNUNET_free (dk);
    489   return GNUNET_OK;
    490 }
    491 
    492 
    493 /**
    494  * Free donation unit key data.
    495  *
    496  * @param cls a `struct DH_KeyStateHandle`, unused
    497  * @param pid the online signing key (type-disguised), unused
    498  * @param value a `struct SigningKey` to free
    499  * @return #GNUNET_OK (continue to iterate)
    500  */
    501 static enum GNUNET_GenericReturnValue
    502 clear_signkey_cb (void *cls,
    503                   const struct GNUNET_PeerIdentity *pid,
    504                   void *value)
    505 {
    506   struct SigningKey *sk = value;
    507 
    508   (void) cls;
    509   (void) pid;
    510   GNUNET_free (sk);
    511   return GNUNET_OK;
    512 }
    513 
    514 
    515 /**
    516  * Synchronize helper state. Polls the key helper for updates.
    517  */
    518 static void
    519 sync_key_helpers (void)
    520 {
    521   TALER_CRYPTO_helper_rsa_poll (rsadh);
    522   TALER_CRYPTO_helper_cs_poll (csdh);
    523   TALER_CRYPTO_helper_esign_poll (esh);
    524 }
    525 
    526 
    527 /**
    528  * Check that the given RSA security module's public key is the one
    529  * we have pinned.  If it does not match, we die hard.
    530  *
    531  * @param sm_pub RSA security module public key to check
    532  */
    533 static void
    534 check_donation_unit_rsa_sm_pub (const struct
    535                                 TALER_SecurityModulePublicKeyP *sm_pub)
    536 {
    537   if (0 !=
    538       GNUNET_memcmp (sm_pub,
    539                      &donation_unit_rsa_sm_pub))
    540   {
    541     if (! GNUNET_is_zero (&donation_unit_rsa_sm_pub))
    542     {
    543       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    544                   "Our RSA security module changed its key. This must not happen.\n")
    545       ;
    546       GNUNET_assert (0);
    547     }
    548     donation_unit_rsa_sm_pub = *sm_pub; /* TOFU ;-) */
    549   }
    550 }
    551 
    552 
    553 /**
    554  * Check that the given CS security module's public key is the one
    555  * we have pinned.  If it does not match, we die hard.
    556  *
    557  * @param sm_pub RSA security module public key to check
    558  */
    559 static void
    560 check_donation_unit_cs_sm_pub (const struct
    561                                TALER_SecurityModulePublicKeyP *sm_pub)
    562 {
    563   if (0 !=
    564       GNUNET_memcmp (sm_pub,
    565                      &donation_unit_cs_sm_pub))
    566   {
    567     if (! GNUNET_is_zero (&donation_unit_cs_sm_pub))
    568     {
    569       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    570                   "Our CS security module changed its key. This must not happen.\n")
    571       ;
    572       GNUNET_assert (0);
    573     }
    574     donation_unit_cs_sm_pub = *sm_pub; /* TOFU ;-) */
    575   }
    576 }
    577 
    578 
    579 /**
    580  * Check that the given EdDSA security module's public key is the one
    581  * we have pinned.  If it does not match, we die hard.
    582  *
    583  * @param sm_pub EdDSA security module public key to check
    584  */
    585 static void
    586 check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
    587 {
    588   if (0 !=
    589       GNUNET_memcmp (sm_pub,
    590                      &esign_sm_pub))
    591   {
    592     if (! GNUNET_is_zero (&esign_sm_pub))
    593     {
    594       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    595                   "Our EdDSA security module changed its key. This must not happen.\n")
    596       ;
    597       GNUNET_assert (0);
    598     }
    599     esign_sm_pub = *sm_pub; /* TOFU ;-) */
    600   }
    601 }
    602 
    603 
    604 void
    605 DH_keys_finished ()
    606 {
    607   if (NULL != rsadh)
    608   {
    609     TALER_CRYPTO_helper_rsa_disconnect (rsadh);
    610     rsadh = NULL;
    611   }
    612   if (NULL != csdh)
    613   {
    614     TALER_CRYPTO_helper_cs_disconnect (csdh);
    615     csdh = NULL;
    616   }
    617   if (NULL != esh)
    618   {
    619     TALER_CRYPTO_helper_esign_disconnect (esh);
    620     esh = NULL;
    621   }
    622   if (NULL != du_keys)
    623   {
    624     GNUNET_CONTAINER_multihashmap_iterate (du_keys,
    625                                            &clear_donation_unit_cb,
    626                                            NULL);
    627     GNUNET_CONTAINER_multihashmap_destroy (du_keys);
    628     du_keys = NULL;
    629   }
    630   if (NULL != esign_keys)
    631   {
    632     GNUNET_CONTAINER_multipeermap_iterate (esign_keys,
    633                                            &clear_signkey_cb,
    634                                            NULL);
    635     GNUNET_CONTAINER_multipeermap_destroy (esign_keys);
    636     esign_keys = NULL;
    637   }
    638 }
    639 
    640 
    641 static void
    642 destroy_key_state (struct DH_KeyStateHandle *ksh)
    643 {
    644   if (NULL != ksh->response_compressed)
    645     MHD_destroy_response (ksh->response_compressed);
    646   if (NULL != ksh->response_uncompressed)
    647     MHD_destroy_response (ksh->response_uncompressed);
    648   GNUNET_free (ksh->etag);
    649   GNUNET_free (ksh);
    650 }
    651 
    652 
    653 /**
    654  * Function called with information about available keys for signing.  Usually
    655  * only called once per key upon connect. Also called again in case a key is
    656  * being revoked, in that case with an @a end_time of zero.
    657  *
    658  * @param cls NULL
    659  * @param section_name name of the donation_unit type in the configuration;
    660  *                 NULL if the key has been revoked or purged
    661  * @param start_time when does the key become available for signing;
    662  *                 zero if the key has been revoked or purged
    663  * @param validity_duration how long does the key remain available for signing;
    664  *                 zero if the key has been revoked or purged
    665  * @param h_rsa hash of the @a donation_unit_pub that is available (or was purged)
    666  * @param bs_pub the public key itself, NULL if the key was revoked or purged
    667  * @param sm_pub public key of the security module, NULL if the key was revoked or purged
    668  * @param sm_sig signature from the security module
    669  */
    670 static void
    671 helper_rsa_cb (
    672   void *cls,
    673   const char *section_name,
    674   struct GNUNET_TIME_Timestamp start_time,
    675   struct GNUNET_TIME_Relative validity_duration,
    676   const struct TALER_RsaPubHashP *h_rsa,
    677   struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
    678   const struct TALER_SecurityModulePublicKeyP *sm_pub,
    679   const struct TALER_SecurityModuleSignatureP *sm_sig)
    680 {
    681   struct DH_DonationUnitKey *du;
    682   struct TALER_Amount value;
    683   enum GNUNET_DB_QueryStatus qs;
    684 
    685   (void) cls;
    686   (void) sm_sig; /* not using offline signatures */
    687   GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher);
    688   if (GNUNET_OK !=
    689       TALER_config_get_amount (DH_cfg,
    690                                section_name,
    691                                "value",
    692                                &value))
    693   {
    694     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    695                 "RSA helper provided key for configuration section `%s' that has no `value' option set\n",
    696                 section_name);
    697     return;
    698   }
    699   /* FIXME: could additionally sanity-check that this
    700      section actually has CIPHER = RSA, etc. */
    701   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    702               "RSA helper announces key %s for donation_unit type %s with validity %s\n",
    703               GNUNET_h2s (&h_rsa->hash),
    704               section_name,
    705               GNUNET_STRINGS_relative_time_to_string (validity_duration,
    706                                                       false));
    707   du = GNUNET_CONTAINER_multihashmap_get (du_keys,
    708                                           &h_rsa->hash);
    709   if (NULL != du)
    710   {
    711     /* only update 'lost' status */
    712     du->lost = GNUNET_TIME_relative_is_zero (validity_duration);
    713     return;
    714   }
    715   GNUNET_assert (NULL != sm_pub);
    716   check_donation_unit_rsa_sm_pub (sm_pub);
    717 
    718   du = GNUNET_new (struct DH_DonationUnitKey);
    719   du->h_donation_unit_pub.hash = h_rsa->hash;
    720   du->donation_unit_pub.bsign_pub_key
    721     = GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
    722   du->validity_year = GNUNET_TIME_time_to_year (start_time.abs_time);
    723   du->value = value;
    724   du->lost = GNUNET_TIME_relative_is_zero (validity_duration);
    725 
    726   GNUNET_assert (
    727     GNUNET_OK ==
    728     GNUNET_CONTAINER_multihashmap_put (
    729       du_keys,
    730       &du->h_donation_unit_pub.hash,
    731       du,
    732       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    733 
    734   qs = DONAUDB_insert_donation_unit (DH_context,
    735                                      &du->h_donation_unit_pub,
    736                                      &du->donation_unit_pub,
    737                                      du->validity_year,
    738                                      &du->value);
    739   if (qs < 0)
    740   {
    741     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    742                 "Failed to insert donation units\n");
    743     GNUNET_SCHEDULER_shutdown ();
    744     DH_global_ret = EXIT_FAILURE;
    745     return;
    746   }
    747   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    748               "Inserted RSA donation unit of %s\n",
    749               TALER_amount2s (&value));
    750   key_generation++;
    751 }
    752 
    753 
    754 /**
    755  * Function called with information about available CS keys for signing. Usually
    756  * only called once per key upon connect. Also called again in case a key is
    757  * being revoked, in that case with an @a end_time of zero.
    758  *
    759  * @param cls NULL
    760  * @param section_name name of the donation unit type in the configuration;
    761  *                 NULL if the key has been revoked or purged
    762  * @param start_time when does the key become available for signing;
    763  *                 zero if the key has been revoked or purged
    764  * @param validity_duration how long does the key remain available for signing;
    765  *                 zero if the key has been revoked or purged
    766  * @param h_cs hash of the @a donation_unit_pub that is available (or was purged)
    767  * @param bs_pub the public key itself, NULL if the key was revoked or purged
    768  * @param sm_pub the public key of the security module
    769  * @param sm_sig signature by the security module
    770  */
    771 static void
    772 helper_cs_cb (
    773   void *cls,
    774   const char *section_name,
    775   struct GNUNET_TIME_Timestamp start_time,
    776   struct GNUNET_TIME_Relative validity_duration,
    777   const struct TALER_CsPubHashP *h_cs,
    778   struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
    779   const struct TALER_SecurityModulePublicKeyP *sm_pub,
    780   const struct TALER_SecurityModuleSignatureP *sm_sig)
    781 {
    782   struct DH_DonationUnitKey *du;
    783   struct TALER_Amount value;
    784   enum GNUNET_DB_QueryStatus qs;
    785 
    786   (void) cls;
    787   (void) sm_sig; /* we are not using offline signatures */
    788   GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher);
    789   if (GNUNET_OK !=
    790       TALER_config_get_amount (DH_cfg,
    791                                section_name,
    792                                "value",
    793                                &value))
    794   {
    795     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    796                 "CS helper provided key for configuration section `%s' that has no `value' option set\n",
    797                 section_name);
    798     return;
    799   }
    800   /* FIXME: could additionally sanity-check that this
    801      section actually has CIPHER = CS, etc. */
    802 
    803   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    804               "CS helper announces key %s for donation unit type %s with validity %s\n",
    805               GNUNET_h2s (&h_cs->hash),
    806               section_name,
    807               GNUNET_STRINGS_relative_time_to_string (validity_duration,
    808                                                       false));
    809   du = GNUNET_CONTAINER_multihashmap_get (du_keys,
    810                                           &h_cs->hash);
    811   if (NULL != du)
    812   {
    813     /* should be just an update (revocation!), so update existing entry */
    814     du->lost = GNUNET_TIME_relative_is_zero (validity_duration);
    815     return;
    816   }
    817   GNUNET_assert (NULL != sm_pub);
    818   check_donation_unit_cs_sm_pub (sm_pub);
    819 
    820   du = GNUNET_new (struct DH_DonationUnitKey);
    821   du->h_donation_unit_pub.hash = h_cs->hash;
    822   du->donation_unit_pub.bsign_pub_key
    823     = GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
    824   du->validity_year = GNUNET_TIME_time_to_year (start_time.abs_time);
    825   du->value = value;
    826   du->lost = GNUNET_TIME_relative_is_zero (validity_duration);
    827   GNUNET_assert (
    828     GNUNET_OK ==
    829     GNUNET_CONTAINER_multihashmap_put (
    830       du_keys,
    831       &du->h_donation_unit_pub.hash,
    832       du,
    833       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    834 
    835   qs = DONAUDB_insert_donation_unit (DH_context,
    836                                      &du->h_donation_unit_pub,
    837                                      &du->donation_unit_pub,
    838                                      du->validity_year,
    839                                      &du->value);
    840   if (qs < 0)
    841   {
    842     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    843                 "Failed to insert donation units\n");
    844     GNUNET_SCHEDULER_shutdown ();
    845     DH_global_ret = EXIT_FAILURE;
    846     return;
    847   }
    848   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    849               "Inserted CS donation unit of %s\n",
    850               TALER_amount2s (&value));
    851 
    852   key_generation++;
    853 }
    854 
    855 
    856 /**
    857  * Function called with information about available keys for signing.  Usually
    858  * only called once per key upon connect. Also called again in case a key is
    859  * being revoked, in that case with an @a end_time of zero.
    860  *
    861  * @param cls NULL
    862  * @param start_time when does the key become available for signing;
    863  *                 zero if the key has been revoked or purged
    864  * @param validity_duration how long does the key remain available for signing;
    865  *                 zero if the key has been revoked or purged
    866  * @param donau_pub the public key itself, NULL if the key was revoked or purged
    867  * @param sm_pub public key of the security module, NULL if the key was revoked or purged
    868  * @param sm_sig signature from the security module
    869  */
    870 static void
    871 helper_esign_cb (
    872   void *cls,
    873   struct GNUNET_TIME_Timestamp start_time,
    874   struct GNUNET_TIME_Relative validity_duration,
    875   const struct TALER_ExchangePublicKeyP *donau_pub,
    876   const struct TALER_SecurityModulePublicKeyP *sm_pub,
    877   const struct TALER_SecurityModuleSignatureP *sm_sig)
    878 {
    879   struct SigningKey *sk;
    880   struct GNUNET_PeerIdentity pid;
    881   unsigned long long expire_legal;
    882   /* need to "cast" because secmod works with TALER_ExchangePublicKeyP */
    883   struct DONAU_DonauPublicKeyP donau_pubkey = {
    884     .eddsa_pub = donau_pub->eddsa_pub
    885   };
    886   enum GNUNET_DB_QueryStatus qs;
    887 
    888   (void) cls;
    889   (void) sm_sig; /* not using offline signing */
    890   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    891               "EdDSA helper announces signing key %s with validity %s\n",
    892               TALER_B2S (donau_pub),
    893               GNUNET_STRINGS_relative_time_to_string (validity_duration,
    894                                                       false));
    895 
    896   pid.public_key = donau_pub->eddsa_pub;
    897   sk = GNUNET_CONTAINER_multipeermap_get (esign_keys,
    898                                           &pid);
    899   if (NULL != sk)
    900   {
    901     /* should be just an update (revocation!), so update existing entry */
    902     sk->lost = GNUNET_TIME_relative_is_zero (validity_duration);
    903     return;
    904   }
    905   GNUNET_assert (NULL != sm_pub);
    906   check_esign_sm_pub (sm_pub);
    907 
    908   sk = GNUNET_new (struct SigningKey);
    909   sk->donau_pub = donau_pubkey;
    910   sk->meta.valid_from = start_time;
    911   sk->meta.expire_sign
    912     = GNUNET_TIME_absolute_to_timestamp (
    913         GNUNET_TIME_absolute_add (start_time.abs_time,
    914                                   validity_duration));
    915 
    916   if (GNUNET_OK !=
    917       GNUNET_CONFIGURATION_get_value_number (DH_cfg,
    918                                              "donau",
    919                                              "EXPIRE_LEGAL_YEARS",
    920                                              &expire_legal))
    921   {
    922     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    923                 "Need EXPIRE_LEGAL_YEARS in section `donau'\n");
    924     GNUNET_SCHEDULER_shutdown ();
    925     DH_global_ret = EXIT_FAILURE;
    926     return;
    927   }
    928   sk->meta.expire_legal
    929     = GNUNET_TIME_absolute_to_timestamp (
    930         GNUNET_TIME_absolute_add (start_time.abs_time,
    931                                   GNUNET_TIME_relative_multiply (
    932                                     GNUNET_TIME_UNIT_YEARS,
    933                                     expire_legal)));
    934 
    935   GNUNET_assert (
    936     GNUNET_OK ==
    937     GNUNET_CONTAINER_multipeermap_put (
    938       esign_keys,
    939       &pid,
    940       sk,
    941       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    942 
    943   qs = DONAUDB_insert_signing_key (DH_context,
    944                                    &donau_pubkey,
    945                                    &sk->meta);
    946   if (qs < 0)
    947   {
    948     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    949                 "Failed to insert donation units\n");
    950     GNUNET_SCHEDULER_shutdown ();
    951     DH_global_ret = EXIT_FAILURE;
    952     return;
    953   }
    954 
    955   key_generation++;
    956 }
    957 
    958 
    959 enum GNUNET_GenericReturnValue
    960 DH_keys_init ()
    961 {
    962   du_keys
    963     = GNUNET_CONTAINER_multihashmap_create (1024,
    964                                             GNUNET_YES);
    965   esign_keys
    966     = GNUNET_CONTAINER_multipeermap_create (32,
    967                                             GNUNET_NO /* MUST BE NO! */);
    968   rsadh = TALER_CRYPTO_helper_rsa_connect (DH_cfg,
    969                                            "donau",
    970                                            &helper_rsa_cb,
    971                                            NULL);
    972   if (NULL == rsadh)
    973   {
    974     GNUNET_break (0);
    975     DH_keys_finished ();
    976     return GNUNET_SYSERR;
    977   }
    978   csdh = TALER_CRYPTO_helper_cs_connect (DH_cfg,
    979                                          "donau",
    980                                          &helper_cs_cb,
    981                                          NULL);
    982   if (NULL == csdh)
    983   {
    984     GNUNET_break (0);
    985     DH_keys_finished ();
    986     return GNUNET_SYSERR;
    987   }
    988   esh = TALER_CRYPTO_helper_esign_connect (DH_cfg,
    989                                            "donau",
    990                                            &helper_esign_cb,
    991                                            NULL);
    992   if (NULL == esh)
    993   {
    994     GNUNET_break (0);
    995     DH_keys_finished ();
    996     return GNUNET_SYSERR;
    997   }
    998   return GNUNET_OK;
    999 }
   1000 
   1001 
   1002 /**
   1003  * Function called with information about the donau's donation_unit keys.
   1004  *
   1005  * @param cls NULL
   1006  * @param donation_unit_pub public key of the donation_unit
   1007  * @param h_donation_unit_pub hash of @a donation_unit_pub
   1008  * @param validity_year of the donation unit
   1009  * @param value of the donation unit
   1010  */
   1011 static enum GNUNET_GenericReturnValue
   1012 donation_unit_info_cb (
   1013   void *cls,
   1014   const struct DONAU_DonationUnitHashP *h_donation_unit_pub,
   1015   const struct DONAU_DonationUnitPublicKey *donation_unit_pub,
   1016   uint64_t validity_year,
   1017   struct TALER_Amount *value)
   1018 {
   1019   struct DH_DonationUnitKey *du;
   1020 
   1021   (void) cls;
   1022   GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID !=
   1023                  donation_unit_pub->bsign_pub_key->cipher);
   1024   du = GNUNET_CONTAINER_multihashmap_get (du_keys,
   1025                                           &h_donation_unit_pub->hash);
   1026   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1027               "Got %s key from database\n",
   1028               NULL == du ? "unknown" : "known");
   1029   if (NULL != du)
   1030   {
   1031     /* we already know this, nothing to do */
   1032     return GNUNET_OK;
   1033   }
   1034 
   1035   du = GNUNET_new (struct DH_DonationUnitKey);
   1036   du->h_donation_unit_pub = *h_donation_unit_pub;
   1037   DONAU_donation_unit_pub_deep_copy (&du->donation_unit_pub,
   1038                                      donation_unit_pub);
   1039   du->validity_year = validity_year;
   1040   du->value = *value;
   1041   du->lost = true; /* no private key known, that can only come from the helper! */
   1042   GNUNET_assert (
   1043     GNUNET_OK ==
   1044     GNUNET_CONTAINER_multihashmap_put (du_keys,
   1045                                        &du->h_donation_unit_pub.hash,
   1046                                        du,
   1047                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
   1048     );
   1049   return GNUNET_OK;
   1050 }
   1051 
   1052 
   1053 /**
   1054  * Function called with information about the donau's online signing keys.
   1055  *
   1056  * @param cls NULL
   1057  * @param donau_pub the public key
   1058  * @param meta meta data information about the denomination type (expirations)
   1059  */
   1060 static void
   1061 iterate_active_signing_keys_cb (
   1062   void *cls,
   1063   const struct DONAU_DonauPublicKeyP *donau_pub,
   1064   struct DONAUDB_SignkeyMetaData *meta)
   1065 {
   1066   /* The 'pid' is used as the key in the "peer" map... */
   1067   struct GNUNET_PeerIdentity pid = {
   1068     .public_key = donau_pub->eddsa_pub
   1069   };
   1070   struct SigningKey *sk;
   1071 
   1072   (void) cls;
   1073   sk = GNUNET_CONTAINER_multipeermap_get (esign_keys,
   1074                                           &pid);
   1075   if (NULL != sk)
   1076   {
   1077     /* should be just an update (revocation!), so update existing entry */
   1078     return;
   1079   }
   1080   sk = GNUNET_new (struct SigningKey);
   1081   sk->donau_pub = *donau_pub;
   1082   sk->meta = *meta;
   1083   sk->lost = true;  /* no private key known, that can only come from the helper! */
   1084   GNUNET_assert (
   1085     GNUNET_OK ==
   1086     GNUNET_CONTAINER_multipeermap_put (esign_keys,
   1087                                        &pid,
   1088                                        sk,
   1089                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
   1090     );
   1091 }
   1092 
   1093 
   1094 /**
   1095  * Create a key state.
   1096  *
   1097  * @return NULL on error (i.e. failed to access database)
   1098  */
   1099 static struct DH_KeyStateHandle *
   1100 build_key_state ()
   1101 {
   1102   struct DH_KeyStateHandle *ksh;
   1103   enum GNUNET_DB_QueryStatus qs;
   1104 
   1105   ksh = GNUNET_new (struct DH_KeyStateHandle);
   1106   ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS;
   1107   ksh->reload_time = GNUNET_TIME_timestamp_get ();
   1108   /* We must use the key_generation from when we STARTED the process! */
   1109   ksh->key_generation = key_generation;
   1110 
   1111   /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */
   1112   GNUNET_break (GNUNET_OK ==
   1113                 DONAUDB_preflight (DH_context));
   1114   qs = DONAUDB_iterate_donation_units (DH_context,
   1115                                        &donation_unit_info_cb,
   1116                                        NULL);
   1117   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1118               "Fetched %d donation unit keys from DB\n",
   1119               (int) qs);
   1120   if (qs < 0)
   1121   {
   1122     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   1123     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
   1124     destroy_key_state (ksh);
   1125     return NULL;
   1126   }
   1127 
   1128   /* NOTE: ONLY fetches active signkeys! */
   1129   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1130               "Fetching active signing keys from DB\n");
   1131   qs = DONAUDB_iterate_active_signing_keys (DH_context,
   1132                                             &iterate_active_signing_keys_cb,
   1133                                             NULL);
   1134   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1135               "Fetched %d active signing keys from DB\n",
   1136               (int) qs);
   1137   if (qs < 0)
   1138   {
   1139     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   1140     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
   1141     destroy_key_state (ksh);
   1142     return NULL;
   1143   }
   1144 
   1145   if (GNUNET_OK !=
   1146       finish_keys_response (ksh))
   1147   {
   1148     GNUNET_log (
   1149       GNUNET_ERROR_TYPE_WARNING,
   1150       "Could not finish /keys response (likely no signing keys available yet)\n");
   1151     destroy_key_state (ksh);
   1152     return NULL;
   1153   }
   1154 
   1155   return ksh;
   1156 }
   1157 
   1158 
   1159 static struct DH_KeyStateHandle*
   1160 DH_keys_get_state ()
   1161 {
   1162   struct DH_KeyStateHandle *old_ksh;
   1163   struct DH_KeyStateHandle *ksh;
   1164 
   1165   old_ksh = key_state;
   1166   if (NULL == old_ksh)
   1167   {
   1168     ksh = build_key_state ();
   1169     if (NULL == ksh)
   1170       return NULL;
   1171     key_state = ksh;
   1172     return ksh;
   1173   }
   1174   if ( (old_ksh->key_generation < key_generation) ||
   1175        (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) )
   1176   {
   1177     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1178                 "Rebuilding /keys, generation upgrade from %llu to %llu\n",
   1179                 (unsigned long long ) old_ksh->key_generation,
   1180                 (unsigned long long ) key_generation);
   1181     ksh = build_key_state ();
   1182     key_state = ksh;
   1183     destroy_key_state (old_ksh);
   1184     return ksh;
   1185   }
   1186   return old_ksh;
   1187 }
   1188 
   1189 
   1190 enum MHD_Result
   1191 DH_handler_get_keys (struct DH_RequestContext *rc,
   1192                      const char *const args[])
   1193 {
   1194   struct MHD_Connection *connection = rc->connection;
   1195   struct DH_KeyStateHandle *ksh;
   1196   struct MHD_Response *resp;
   1197 
   1198   (void) args;
   1199   sync_key_helpers ();
   1200   ksh = DH_keys_get_state ();
   1201   if (NULL == ksh)
   1202   {
   1203     if ( (GNUNET_is_zero (&donation_unit_rsa_sm_pub)) &&
   1204          (GNUNET_is_zero (&donation_unit_cs_sm_pub)) )
   1205     {
   1206       /* Either IPC failed, or neither helper had any donation_unit configured. */
   1207       return TALER_MHD_reply_with_error (connection,
   1208                                          MHD_HTTP_BAD_GATEWAY,
   1209                                          TALER_EC_DONAU_DONATION_UNIT_HELPER_UNAVAILABLE,
   1210                                          NULL);
   1211     }
   1212     if (GNUNET_is_zero (&esign_sm_pub))
   1213     {
   1214       return TALER_MHD_reply_with_error (connection,
   1215                                          MHD_HTTP_BAD_GATEWAY,
   1216                                          TALER_EC_DONAU_SIGNKEY_HELPER_UNAVAILABLE,
   1217                                          NULL);
   1218     }
   1219     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1220                 "Failed to build /keys response?\n");
   1221     return TALER_MHD_reply_with_error (connection,
   1222                                        MHD_HTTP_SERVICE_UNAVAILABLE,
   1223                                        TALER_EC_DONAU_GENERIC_KEYS_MISSING,
   1224                                        "failed to create keys response");
   1225   }
   1226   resp = (TALER_MHD_CT_DEFLATE ==
   1227           TALER_MHD_can_compress (connection,
   1228                                   TALER_MHD_CT_DEFLATE))
   1229     ? ksh->response_compressed
   1230     : ksh->response_uncompressed;
   1231   GNUNET_assert (NULL != resp);
   1232   return MHD_queue_response (connection,
   1233                              MHD_HTTP_OK,
   1234                              resp);
   1235 
   1236 }
   1237 
   1238 
   1239 enum TALER_ErrorCode
   1240 DH_keys_donau_sign_ (
   1241   const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
   1242   struct DONAU_DonauPublicKeyP *pub,
   1243   struct DONAU_DonauSignatureP *sig)
   1244 {
   1245   struct DH_KeyStateHandle *ksh;
   1246   enum TALER_ErrorCode ec;
   1247 
   1248   ksh = DH_keys_get_state ();
   1249   if (NULL == ksh)
   1250   {
   1251     /* This *can* happen if the Donau's crypto helper is not running
   1252        or had some bad error. */
   1253     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1254                 "Cannot sign request, no valid signing keys available.\n");
   1255     return TALER_EC_DONAU_GENERIC_KEYS_MISSING;
   1256   }
   1257 
   1258   {
   1259     /* need to "cast" because TALER_CRYPTO works with TALER_Exchange.. */
   1260     struct TALER_ExchangePublicKeyP donau_pub;
   1261     struct TALER_ExchangeSignatureP donau_sig;
   1262 
   1263     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
   1264                                           purpose,
   1265                                           &donau_pub,
   1266                                           &donau_sig);
   1267     if (TALER_EC_NONE != ec)
   1268       return ec;
   1269     pub->eddsa_pub = donau_pub.eddsa_pub;
   1270     sig->eddsa_sig = donau_sig.eddsa_signature;
   1271   }
   1272   return ec;
   1273 }
   1274 
   1275 
   1276 enum TALER_ErrorCode
   1277 DH_keys_donation_unit_batch_sign (
   1278   unsigned int num_bkps,
   1279   const struct DONAU_BkpSignData bkps[num_bkps],
   1280   struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps])
   1281 {
   1282   struct DH_KeyStateHandle *ksh;
   1283   struct DH_DonationUnitKey *du;
   1284   struct TALER_CRYPTO_RsaSignRequest rsrs[num_bkps];
   1285   struct TALER_CRYPTO_CsSignRequest csrs[num_bkps];
   1286   struct TALER_BlindedDenominationSignature rs[num_bkps];
   1287   struct TALER_BlindedDenominationSignature cs[num_bkps];
   1288   unsigned int rsrs_pos = 0;
   1289   unsigned int csrs_pos = 0;
   1290   enum TALER_ErrorCode ec;
   1291 
   1292   ksh = DH_keys_get_state ();
   1293   if (NULL == ksh)
   1294     return TALER_EC_DONAU_GENERIC_KEYS_MISSING;
   1295   for (unsigned int i = 0; i<num_bkps; i++)
   1296   {
   1297     const struct DONAU_DonationUnitHashP *h_du_pub =
   1298       bkps[i].h_donation_unit_pub;
   1299     const struct DONAU_BlindedUniqueDonorIdentifier *budi = bkps[i].budi;
   1300 
   1301     du = GNUNET_CONTAINER_multihashmap_get (du_keys,
   1302                                             &h_du_pub->hash);
   1303     if (NULL == du)
   1304       return TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN;
   1305     if (budi->blinded_message->cipher !=
   1306         du->donation_unit_pub.bsign_pub_key->cipher)
   1307       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
   1308     switch (du->donation_unit_pub.bsign_pub_key->cipher)
   1309     {
   1310     case GNUNET_CRYPTO_BSA_RSA:
   1311       /* See DONAU_donation_unit_pub_hash: we guarantee that these
   1312          hashes are equivalent! */
   1313       rsrs[rsrs_pos].h_rsa
   1314         = (const struct TALER_RsaPubHashP *) &du->h_donation_unit_pub;
   1315       rsrs[rsrs_pos].msg
   1316         = budi->blinded_message->details.rsa_blinded_message.blinded_msg;
   1317       rsrs[rsrs_pos].msg_size
   1318         = budi->blinded_message->details.rsa_blinded_message.blinded_msg_size;
   1319       rsrs_pos++;
   1320       break;
   1321     case GNUNET_CRYPTO_BSA_CS:
   1322       /* See DONAU_donation_unit_pub_hash: we guarantee that these
   1323          hashes are equivalent! */
   1324       csrs[csrs_pos].h_cs
   1325         = (const struct TALER_CsPubHashP *) &du->h_donation_unit_pub;
   1326       csrs[csrs_pos].blinded_planchet
   1327         = &budi->blinded_message->details.cs_blinded_message;
   1328       csrs_pos++;
   1329       break;
   1330     default:
   1331       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
   1332     }
   1333   }
   1334 
   1335   if ( (0 != csrs_pos) &&
   1336        (0 != rsrs_pos) )
   1337   {
   1338     memset (rs,
   1339             0,
   1340             sizeof (rs));
   1341     memset (cs,
   1342             0,
   1343             sizeof (cs));
   1344   }
   1345   ec = TALER_EC_NONE;
   1346   if (0 != csrs_pos)
   1347   {
   1348     ec = TALER_CRYPTO_helper_cs_batch_sign (
   1349       csdh,
   1350       csrs_pos,
   1351       csrs,
   1352       false, // for_melt
   1353       cs);
   1354     if (TALER_EC_NONE != ec)
   1355     {
   1356       for (unsigned int i = 0; i<csrs_pos; i++)
   1357       {
   1358         if (NULL != cs[i].blinded_sig)
   1359         {
   1360           GNUNET_CRYPTO_blinded_sig_decref (cs[i].blinded_sig);
   1361           cs[i].blinded_sig = NULL;
   1362         }
   1363       }
   1364       return ec;
   1365     }
   1366   }
   1367   if (0 != rsrs_pos)
   1368   {
   1369     ec = TALER_CRYPTO_helper_rsa_batch_sign (
   1370       rsadh,
   1371       rsrs_pos,
   1372       rsrs,
   1373       rs);
   1374     if (TALER_EC_NONE != ec)
   1375     {
   1376       for (unsigned int i = 0; i<csrs_pos; i++)
   1377       {
   1378         if (NULL != cs[i].blinded_sig)
   1379         {
   1380           GNUNET_CRYPTO_blinded_sig_decref (cs[i].blinded_sig);
   1381           cs[i].blinded_sig = NULL;
   1382         }
   1383       }
   1384       for (unsigned int i = 0; i<rsrs_pos; i++)
   1385       {
   1386         if (NULL != rs[i].blinded_sig)
   1387         {
   1388           GNUNET_CRYPTO_blinded_sig_decref (rs[i].blinded_sig);
   1389           rs[i].blinded_sig = NULL;
   1390         }
   1391       }
   1392       return ec;
   1393     }
   1394   }
   1395 
   1396   rsrs_pos = 0;
   1397   csrs_pos = 0;
   1398   for (unsigned int i = 0; i<num_bkps; i++)
   1399   {
   1400     const struct DONAU_BlindedUniqueDonorIdentifier *budi = bkps[i].budi;
   1401 
   1402     switch (budi->blinded_message->cipher)
   1403     {
   1404     case GNUNET_CRYPTO_BSA_RSA:
   1405       du_sigs[i].blinded_sig = rs[rsrs_pos++].blinded_sig;
   1406       break;
   1407     case GNUNET_CRYPTO_BSA_CS:
   1408       du_sigs[i].blinded_sig = cs[csrs_pos++].blinded_sig;
   1409       break;
   1410     default:
   1411       GNUNET_assert (0);
   1412     }
   1413   }
   1414   return TALER_EC_NONE;
   1415 }
   1416 
   1417 
   1418 struct DH_DonationUnitKey *
   1419 DH_keys_donation_unit_by_hash (
   1420   const struct DONAU_DonationUnitHashP *h_du_pub)
   1421 {
   1422   return GNUNET_CONTAINER_multihashmap_get (du_keys,
   1423                                             &h_du_pub->hash);
   1424 }
   1425 
   1426 
   1427 enum TALER_ErrorCode
   1428 DH_keys_donation_unit_cs_r_pub (
   1429   const struct DONAU_DonationUnitHashP *h_donation_unit_pub,
   1430   const struct GNUNET_CRYPTO_CsSessionNonce *nonce,
   1431   struct GNUNET_CRYPTO_CSPublicRPairP *r_pub)
   1432 {
   1433   struct DH_DonationUnitKey *dk;
   1434 
   1435   dk = DH_keys_donation_unit_by_hash (h_donation_unit_pub);
   1436   if (NULL == dk)
   1437   {
   1438     return TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN;
   1439   }
   1440   if (GNUNET_CRYPTO_BSA_CS !=
   1441       dk->donation_unit_pub.bsign_pub_key->cipher)
   1442   {
   1443     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
   1444   }
   1445 
   1446   {
   1447     struct TALER_CRYPTO_CsDeriveRequest cdr = {
   1448       .h_cs = (const struct TALER_CsPubHashP *) &dk->h_donation_unit_pub,
   1449       .nonce = nonce
   1450     };
   1451     return TALER_CRYPTO_helper_cs_r_batch_derive (csdh,
   1452                                                   1,
   1453                                                   &cdr,
   1454                                                   false,
   1455                                                   r_pub);
   1456   }
   1457 }
   1458 
   1459 
   1460 /* end of donau-httpd_get-keys.c */