donau

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

donau-httpd_keys.c (45094B)


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