exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-httpd_get-keys.c (99616B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2020-2025 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 taler-exchange-httpd_get-keys.c
     18  * @brief management of our various keys
     19  * @author Christian Grothoff
     20  * @author Özgür Kesim
     21  */
     22 #include "taler/taler_json_lib.h"
     23 #include "taler/taler_mhd_lib.h"
     24 #include "taler/taler_kyclogic_lib.h"
     25 #include "taler/taler_dbevents.h"
     26 #include "taler-exchange-httpd.h"
     27 #include "exchange-database/preflight.h"
     28 #include "taler-exchange-httpd_get-config.h"
     29 #include "taler-exchange-httpd_get-keys.h"
     30 #include "taler-exchange-httpd_secmod-helpers.h"
     31 #include "taler-exchange-httpd_responses.h"
     32 #include "taler-exchange-httpd_configuration.h"
     33 #include "exchange-database/event_listen.h"
     34 #include "exchange-database/event_listen_cancel.h"
     35 #include "exchange-database/event_notify.h"
     36 struct TEH_KeyStateHandle;
     37 struct AddContext;
     38 #define TALER_EXCHANGEDB_GLOBAL_FEE_RESULT_CLOSURE struct TEH_KeyStateHandle
     39 #define TALER_EXCHANGEDB_WIRE_ACCOUNT_RESULT_CLOSURE json_t
     40 #define TALER_EXCHANGEDB_WIRE_FEE_RESULT_CLOSURE struct AddContext
     41 #define TALER_EXCHANGEDB_AUDITORS_RESULT_CLOSURE struct TEH_KeyStateHandle
     42 #define TALER_EXCHANGEDB_ACTIVE_SIGNKEYS_RESULT_CLOSURE struct \
     43         TEH_KeyStateHandle
     44 #define TALER_EXCHANGEDB_AUDITOR_DENOMINATIONS_RESULT_CLOSURE struct \
     45         TEH_KeyStateHandle
     46 #define TALER_EXCHANGEDB_DENOMINATIONS_RESULT_CLOSURE struct TEH_KeyStateHandle
     47 #include "exchange-database/iterate_global_fees.h"
     48 #include "exchange-database/iterate_wire_accounts.h"
     49 #include "exchange-database/iterate_wire_fees.h"
     50 #include "exchange-database/iterate_active_auditors.h"
     51 #include "exchange-database/iterate_active_signkeys.h"
     52 #include "exchange-database/iterate_auditor_denominations.h"
     53 #include "exchange-database/iterate_denominations.h"
     54 
     55 
     56 /**
     57  * How many /keys request do we hold in suspension at
     58  * most at any time?
     59  */
     60 #define SKR_LIMIT 32
     61 
     62 
     63 /**
     64  * When do we forcefully timeout a /keys request?
     65  * Matches the 120s hard-coded into exchange_api_handle.c
     66  */
     67 #define KEYS_TIMEOUT \
     68         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
     69 
     70 
     71 /**
     72  * Signatures of an auditor over a denomination key of this exchange.
     73  */
     74 struct TEH_AuditorSignature
     75 {
     76   /**
     77    * We store the signatures in a DLL.
     78    */
     79   struct TEH_AuditorSignature *prev;
     80 
     81   /**
     82    * We store the signatures in a DLL.
     83    */
     84   struct TEH_AuditorSignature *next;
     85 
     86   /**
     87    * A signature from the auditor.
     88    */
     89   struct TALER_AuditorSignatureP asig;
     90 
     91   /**
     92    * Public key of the auditor.
     93    */
     94   struct TALER_AuditorPublicKeyP apub;
     95 
     96 };
     97 
     98 
     99 /**
    100  * Entry in (sorted) array with possible pre-build responses for /keys.
    101  * We keep pre-build responses for the various (valid) cherry-picking
    102  * values around.
    103  */
    104 struct KeysResponseData
    105 {
    106 
    107   /**
    108    * Response to return if the client supports (deflate) compression.
    109    */
    110   struct MHD_Response *response_compressed;
    111 
    112   /**
    113    * Response to return if the client does not support compression.
    114    */
    115   struct MHD_Response *response_uncompressed;
    116 
    117   /**
    118    * ETag for these responses.
    119    */
    120   char *etag;
    121 
    122   /**
    123    * Cherry-picking timestamp the client must have set for this
    124    * response to be valid.  0 if this is the "full" response.
    125    * The client's request must include this date or a higher one
    126    * for this response to be applicable.
    127    */
    128   struct GNUNET_TIME_Timestamp cherry_pick_date;
    129 
    130 };
    131 
    132 
    133 /**
    134  * @brief All information about an exchange online signing key (which is used to
    135  * sign messages from the exchange).
    136  */
    137 struct SigningKey
    138 {
    139 
    140   /**
    141    * The exchange's (online signing) public key.
    142    */
    143   struct TALER_ExchangePublicKeyP exchange_pub;
    144 
    145   /**
    146    * Meta data about the signing key, such as validity periods.
    147    */
    148   struct TALER_EXCHANGEDB_SignkeyMetaData meta;
    149 
    150   /**
    151    * The long-term offline master key's signature for this signing key.
    152    * Signs over @e exchange_pub and @e meta.
    153    */
    154   struct TALER_MasterSignatureP master_sig;
    155 
    156 };
    157 
    158 struct TEH_KeyStateHandle
    159 {
    160 
    161   /**
    162    * Mapping from denomination keys to denomination key issue struct.
    163    * Used to lookup the key by hash.
    164    */
    165   struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
    166 
    167   /**
    168    * Mapping from serial ID's to denomination key issue struct.
    169    * Used to lookup the key by serial ID.
    170    *
    171    * FIXME: We need a 64-bit version of this in GNUNET.
    172    */
    173   struct GNUNET_CONTAINER_MultiHashMap32 *denomserial_map;
    174 
    175   /**
    176    * Map from `struct TALER_ExchangePublicKey` to `struct SigningKey`
    177    * entries.  Based on the fact that a `struct GNUNET_PeerIdentity` is also
    178    * an EdDSA public key.
    179    */
    180   struct GNUNET_CONTAINER_MultiPeerMap *signkey_map;
    181 
    182   /**
    183    * Head of DLL of our global fees.
    184    */
    185   struct TEH_GlobalFee *gf_head;
    186 
    187   /**
    188    * Tail of DLL of our global fees.
    189    */
    190   struct TEH_GlobalFee *gf_tail;
    191 
    192   /**
    193    * json array with the auditors of this exchange. Contains exactly
    194    * the information needed for the "auditors" field of the /keys response.
    195    */
    196   json_t *auditors;
    197 
    198   /**
    199    * json array with the global fees of this exchange. Contains exactly
    200    * the information needed for the "global_fees" field of the /keys response.
    201    */
    202   json_t *global_fees;
    203 
    204   /**
    205    * Sorted array of responses to /keys (MUST be sorted by cherry-picking date) of
    206    * length @e krd_array_length;
    207    */
    208   struct KeysResponseData *krd_array;
    209 
    210   /**
    211    * Length of the @e krd_array.
    212    */
    213   unsigned int krd_array_length;
    214 
    215   /**
    216    * Cached reply for a GET /management/keys request.  Used so we do not
    217    * re-create the reply every time.
    218    */
    219   json_t *management_keys_reply;
    220 
    221   /**
    222    * For which (global) key_generation was this data structure created?
    223    * Used to check when we are outdated and need to be re-generated.
    224    */
    225   uint64_t key_generation;
    226 
    227   /**
    228    * When did we initiate the key reloading?
    229    */
    230   struct GNUNET_TIME_Timestamp reload_time;
    231 
    232   /**
    233    * What is the period at which we rotate keys
    234    * (signing or denomination keys)?
    235    */
    236   struct GNUNET_TIME_Relative rekey_frequency;
    237 
    238   /**
    239    * When does our online signing key expire and we
    240    * thus need to re-generate this response?
    241    */
    242   struct GNUNET_TIME_Timestamp signature_expires;
    243 
    244   /**
    245    * True if #finish_keys_response() was not yet run and this key state
    246    * is only suitable for the /management/keys API.
    247    */
    248   bool management_only;
    249 
    250 };
    251 
    252 
    253 /**
    254  * Entry of /keys requests that are currently suspended because we are
    255  * waiting for /keys to become ready.
    256  */
    257 struct SuspendedKeysRequests
    258 {
    259   /**
    260    * Kept in a DLL.
    261    */
    262   struct SuspendedKeysRequests *next;
    263 
    264   /**
    265    * Kept in a DLL.
    266    */
    267   struct SuspendedKeysRequests *prev;
    268 
    269   /**
    270    * The suspended connection.
    271    */
    272   struct MHD_Connection *connection;
    273 
    274   /**
    275    * When does this request timeout?
    276    */
    277   struct GNUNET_TIME_Absolute timeout;
    278 };
    279 
    280 
    281 /**
    282  * Information we track about wire fees.
    283  */
    284 struct WireFeeSet
    285 {
    286 
    287   /**
    288    * Kept in a DLL.
    289    */
    290   struct WireFeeSet *next;
    291 
    292   /**
    293    * Kept in a DLL.
    294    */
    295   struct WireFeeSet *prev;
    296 
    297   /**
    298    * Actual fees.
    299    */
    300   struct TALER_WireFeeSet fees;
    301 
    302   /**
    303    * Start date of fee validity (inclusive).
    304    */
    305   struct GNUNET_TIME_Timestamp start_date;
    306 
    307   /**
    308    * End date of fee validity (exclusive).
    309    */
    310   struct GNUNET_TIME_Timestamp end_date;
    311 
    312   /**
    313    * Wire method the fees apply to.
    314    */
    315   char *method;
    316 };
    317 
    318 
    319 /**
    320  * State we keep per thread to cache the wire part of the /keys response.
    321  */
    322 struct WireStateHandle
    323 {
    324 
    325   /**
    326    * JSON reply for wire response.
    327    */
    328   json_t *json_reply;
    329 
    330   /**
    331    * ETag for this response (if any).
    332    */
    333   char *etag;
    334 
    335   /**
    336    * head of DLL of wire fees.
    337    */
    338   struct WireFeeSet *wfs_head;
    339 
    340   /**
    341    * Tail of DLL of wire fees.
    342    */
    343   struct WireFeeSet *wfs_tail;
    344 
    345   /**
    346    * Earliest timestamp of all the wire methods when we have no more fees.
    347    */
    348   struct GNUNET_TIME_Absolute cache_expiration;
    349 
    350   /**
    351    * @e cache_expiration time, formatted.
    352    */
    353   char dat[128];
    354 
    355   /**
    356    * For which (global) wire_generation was this data structure created?
    357    * Used to check when we are outdated and need to be re-generated.
    358    */
    359   uint64_t wire_generation;
    360 
    361   /**
    362    * Is the wire data ready?
    363    */
    364   bool ready;
    365 
    366 };
    367 
    368 
    369 /**
    370  * Stores the latest generation of our wire response.
    371  */
    372 static struct WireStateHandle *wire_state;
    373 
    374 /**
    375  * Handler listening for wire updates by other exchange
    376  * services.
    377  */
    378 static struct GNUNET_DB_EventHandler *wire_eh;
    379 
    380 /**
    381  * Counter incremented whenever we have a reason to re-build the #wire_state
    382  * because something external changed.
    383  */
    384 static uint64_t wire_generation;
    385 
    386 /**
    387  * Counter incremented whenever we have a reason to re-build the keys because
    388  * something external changed.  See #TEH_keys_get_state() and
    389  * #TEH_keys_update_states() for uses of this variable.
    390  */
    391 static uint64_t key_generation;
    392 
    393 /**
    394  * Stores the latest generation of our key state.
    395  */
    396 static struct TEH_KeyStateHandle *key_state;
    397 
    398 /**
    399  * Handler listening for wire updates by other exchange
    400  * services.
    401  */
    402 static struct GNUNET_DB_EventHandler *keys_eh;
    403 
    404 /**
    405  * Head of DLL of suspended /keys requests.
    406  */
    407 static struct SuspendedKeysRequests *skr_head;
    408 
    409 /**
    410  * Tail of DLL of suspended /keys requests.
    411  */
    412 static struct SuspendedKeysRequests *skr_tail;
    413 
    414 /**
    415  * Number of entries in the @e skr_head DLL.
    416  */
    417 static unsigned int skr_size;
    418 
    419 /**
    420  * Task to force timeouts on /keys requests.
    421  */
    422 static struct GNUNET_SCHEDULER_Task *keys_tt;
    423 
    424 /**
    425  * For how long should a signing key be legally retained?
    426  * Configuration value.
    427  */
    428 static struct GNUNET_TIME_Relative signkey_legal_duration;
    429 
    430 /**
    431  * What type of asset are we dealing with here?
    432  */
    433 static char *asset_type;
    434 
    435 /**
    436  * Are we shutting down?
    437  */
    438 static bool terminating;
    439 
    440 
    441 /**
    442  * Free memory associated with @a wsh
    443  *
    444  * @param[in] wsh wire state to destroy
    445  */
    446 static void
    447 destroy_wire_state (struct WireStateHandle *wsh)
    448 {
    449   struct WireFeeSet *wfs;
    450 
    451   while (NULL != (wfs = wsh->wfs_head))
    452   {
    453     GNUNET_CONTAINER_DLL_remove (wsh->wfs_head,
    454                                  wsh->wfs_tail,
    455                                  wfs);
    456     GNUNET_free (wfs->method);
    457     GNUNET_free (wfs);
    458   }
    459   json_decref (wsh->json_reply);
    460   GNUNET_free (wsh->etag);
    461   GNUNET_free (wsh);
    462 }
    463 
    464 
    465 /**
    466  * Function called whenever another exchange process has updated
    467  * the wire data in the database.
    468  *
    469  * @param cls NULL
    470  * @param extra unused
    471  * @param extra_size number of bytes in @a extra unused
    472  */
    473 static void
    474 wire_update_event_cb (void *cls,
    475                       const void *extra,
    476                       size_t extra_size)
    477 {
    478   (void) cls;
    479   (void) extra;
    480   (void) extra_size;
    481   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    482               "Received wire update event\n");
    483   TEH_check_invariants ();
    484   wire_generation++;
    485   key_generation++;
    486   TEH_resume_keys_requests (false);
    487 }
    488 
    489 
    490 /**
    491  * Add information about a wire account to @a a.
    492  *
    493  * @param a a `json_t *` array to expand with wire account details
    494  * @param payto_uri the exchange bank account URI to add
    495  * @param conversion_url URL of a conversion service, NULL if there is no conversion
    496  * @param open_banking_gateway URL of an open banking gateway, NULL if there is none
    497  * @param prepared_transfer_url URL of a wire transfer gateway, NULL if there is none
    498  * @param debit_restrictions JSON array with debit restrictions on the account
    499  * @param credit_restrictions JSON array with credit restrictions on the account
    500  * @param master_sig master key signature affirming that this is a bank
    501  *                   account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
    502  * @param bank_label label the wallet should use to display the account, can be NULL
    503  * @param priority priority for ordering bank account labels
    504  */
    505 static void
    506 add_wire_account (
    507   json_t *a,
    508   const struct TALER_FullPayto payto_uri,
    509   const char *conversion_url,
    510   const char *open_banking_gateway,
    511   const char *prepared_transfer_url,
    512   const json_t *debit_restrictions,
    513   const json_t *credit_restrictions,
    514   const struct TALER_MasterSignatureP *master_sig,
    515   const char *bank_label,
    516   int64_t priority)
    517 {
    518   if (GNUNET_OK !=
    519       TALER_exchange_wire_signature_check (
    520         payto_uri,
    521         conversion_url,
    522         open_banking_gateway,
    523         prepared_transfer_url,
    524         debit_restrictions,
    525         credit_restrictions,
    526         &TEH_master_public_key,
    527         master_sig))
    528   {
    529     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    530                 "Database has wire account with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
    531     return;
    532   }
    533   if (0 !=
    534       json_array_append_new (
    535         a,
    536         GNUNET_JSON_PACK (
    537           TALER_JSON_pack_full_payto (
    538             "payto_uri",
    539             payto_uri),
    540           GNUNET_JSON_pack_allow_null (
    541             GNUNET_JSON_pack_string (
    542               "conversion_url",
    543               conversion_url)),
    544           GNUNET_JSON_pack_allow_null (
    545             GNUNET_JSON_pack_string (
    546               "open_banking_gateway",
    547               open_banking_gateway)),
    548           GNUNET_JSON_pack_allow_null (
    549             GNUNET_JSON_pack_string (
    550               "prepared_transfer_url",
    551               prepared_transfer_url)),
    552           GNUNET_JSON_pack_allow_null (
    553             GNUNET_JSON_pack_string (
    554               "bank_label",
    555               bank_label)),
    556           GNUNET_JSON_pack_int64 (
    557             "priority",
    558             priority),
    559           GNUNET_JSON_pack_array_incref (
    560             "debit_restrictions",
    561             (json_t *) debit_restrictions),
    562           GNUNET_JSON_pack_array_incref (
    563             "credit_restrictions",
    564             (json_t *) credit_restrictions),
    565           GNUNET_JSON_pack_data_auto (
    566             "master_sig",
    567             master_sig))))
    568   {
    569     GNUNET_break (0);   /* out of memory!? */
    570     return;
    571   }
    572 }
    573 
    574 
    575 /**
    576  * Closure for #add_wire_fee().
    577  */
    578 struct AddContext
    579 {
    580   /**
    581    * Wire method the fees are for.
    582    */
    583   char *wire_method;
    584 
    585   /**
    586    * Wire state we are building.
    587    */
    588   struct WireStateHandle *wsh;
    589 
    590   /**
    591    * Array to append the fee to.
    592    */
    593   json_t *a;
    594 
    595   /**
    596    * Set to the maximum end-date seen.
    597    */
    598   struct GNUNET_TIME_Absolute max_seen;
    599 };
    600 
    601 
    602 /**
    603  * Add information about a wire account to @a ac.
    604  *
    605  * @param ac a `struct AddContext`
    606  * @param fees the wire fees we charge
    607  * @param start_date from when are these fees valid (start date)
    608  * @param end_date until when are these fees valid (end date, exclusive)
    609  * @param master_sig master key signature affirming that this is the correct
    610  *                   fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES)
    611  */
    612 static void
    613 add_wire_fee (struct AddContext *ac,
    614               const struct TALER_WireFeeSet *fees,
    615               struct GNUNET_TIME_Timestamp start_date,
    616               struct GNUNET_TIME_Timestamp end_date,
    617               const struct TALER_MasterSignatureP *master_sig)
    618 {
    619   struct WireFeeSet *wfs;
    620 
    621   if (GNUNET_OK !=
    622       TALER_exchange_offline_wire_fee_verify (
    623         ac->wire_method,
    624         start_date,
    625         end_date,
    626         fees,
    627         &TEH_master_public_key,
    628         master_sig))
    629   {
    630     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    631                 "Database has wire fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
    632     return;
    633   }
    634   ac->max_seen = GNUNET_TIME_absolute_max (ac->max_seen,
    635                                            end_date.abs_time);
    636   wfs = GNUNET_new (struct WireFeeSet);
    637   wfs->start_date = start_date;
    638   wfs->end_date = end_date;
    639   wfs->fees = *fees;
    640   wfs->method = GNUNET_strdup (ac->wire_method);
    641   GNUNET_CONTAINER_DLL_insert (ac->wsh->wfs_head,
    642                                ac->wsh->wfs_tail,
    643                                wfs);
    644   if (0 !=
    645       json_array_append_new (
    646         ac->a,
    647         GNUNET_JSON_PACK (
    648           TALER_JSON_pack_amount ("wire_fee",
    649                                   &fees->wire),
    650           TALER_JSON_pack_amount ("closing_fee",
    651                                   &fees->closing),
    652           GNUNET_JSON_pack_timestamp ("start_date",
    653                                       start_date),
    654           GNUNET_JSON_pack_timestamp ("end_date",
    655                                       end_date),
    656           GNUNET_JSON_pack_data_auto ("sig",
    657                                       master_sig))))
    658   {
    659     GNUNET_break (0);   /* out of memory!? */
    660     return;
    661   }
    662 }
    663 
    664 
    665 /**
    666  * Create the wire response from our database state.
    667  *
    668  * @return NULL on error
    669  */
    670 static struct WireStateHandle *
    671 build_wire_state (void)
    672 {
    673   json_t *wire_accounts_array;
    674   json_t *wire_fee_object;
    675   uint64_t wg = wire_generation; /* must be obtained FIRST */
    676   enum GNUNET_DB_QueryStatus qs;
    677   struct WireStateHandle *wsh;
    678   json_t *wads;
    679 
    680   wsh = GNUNET_new (struct WireStateHandle);
    681   wsh->wire_generation = wg;
    682   wire_accounts_array = json_array ();
    683   GNUNET_assert (NULL != wire_accounts_array);
    684   qs = TALER_EXCHANGEDB_iterate_wire_accounts (TEH_pg,
    685                                                &add_wire_account,
    686                                                wire_accounts_array);
    687   if (0 > qs)
    688   {
    689     GNUNET_break (0);
    690     json_decref (wire_accounts_array);
    691     wsh->ready = false;
    692     return wsh;
    693   }
    694   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    695               "Built wire data with %u accounts (%d)\n",
    696               (unsigned int) json_array_size (wire_accounts_array),
    697               (int) qs);
    698   wire_fee_object = json_object ();
    699   GNUNET_assert (NULL != wire_fee_object);
    700   wsh->cache_expiration = GNUNET_TIME_UNIT_FOREVER_ABS;
    701   {
    702     json_t *account;
    703     size_t index;
    704 
    705     json_array_foreach (wire_accounts_array,
    706                         index,
    707                         account)
    708     {
    709       char *wire_method;
    710       const char *payto_uri = json_string_value (json_object_get (account,
    711                                                                   "payto_uri"));
    712 
    713       GNUNET_assert (NULL != payto_uri);
    714       wire_method = TALER_payto_get_method (payto_uri);
    715       if (NULL == wire_method)
    716       {
    717         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    718                     "No wire method in `%s'\n",
    719                     payto_uri);
    720         wsh->ready = false;
    721         json_decref (wire_accounts_array);
    722         json_decref (wire_fee_object);
    723         return wsh;
    724       }
    725       if (NULL == json_object_get (wire_fee_object,
    726                                    wire_method))
    727       {
    728         struct AddContext ac = {
    729           .wire_method = wire_method,
    730           .wsh = wsh,
    731           .a = json_array ()
    732         };
    733 
    734         GNUNET_assert (NULL != ac.a);
    735         qs = TALER_EXCHANGEDB_iterate_wire_fees (TEH_pg,
    736                                                  wire_method,
    737                                                  &add_wire_fee,
    738                                                  &ac);
    739         if (0 > qs)
    740         {
    741           GNUNET_break (0);
    742           json_decref (ac.a);
    743           json_decref (wire_fee_object);
    744           json_decref (wire_accounts_array);
    745           GNUNET_free (wire_method);
    746           wsh->ready = false;
    747           return wsh;
    748         }
    749         if (0 != json_array_size (ac.a))
    750         {
    751           wsh->cache_expiration
    752             = GNUNET_TIME_absolute_min (ac.max_seen,
    753                                         wsh->cache_expiration);
    754           GNUNET_assert (0 ==
    755                          json_object_set_new (wire_fee_object,
    756                                               wire_method,
    757                                               ac.a));
    758         }
    759         else
    760         {
    761           json_decref (ac.a);
    762         }
    763       }
    764       GNUNET_free (wire_method);
    765     }
    766   }
    767 
    768   wads = json_array (); /* #7271 */
    769   GNUNET_assert (NULL != wads);
    770   wsh->json_reply = GNUNET_JSON_PACK (
    771     GNUNET_JSON_pack_array_steal ("accounts",
    772                                   wire_accounts_array),
    773     GNUNET_JSON_pack_array_steal ("wads",
    774                                   wads),
    775     GNUNET_JSON_pack_object_steal ("fees",
    776                                    wire_fee_object));
    777   wsh->ready = true;
    778   return wsh;
    779 }
    780 
    781 
    782 void
    783 TEH_wire_update_state (void)
    784 {
    785   struct GNUNET_DB_EventHeaderP es = {
    786     .size = htons (sizeof (es)),
    787     .type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED),
    788   };
    789 
    790   TALER_EXCHANGEDB_event_notify (TEH_pg,
    791                                  &es,
    792                                  NULL,
    793                                  0);
    794   wire_generation++;
    795   key_generation++;
    796 }
    797 
    798 
    799 void
    800 TEH_keys_bump_generation (void)
    801 {
    802   key_generation++;
    803 }
    804 
    805 
    806 /**
    807  * Return the current key state for this thread.  Possibly
    808  * re-builds the key state if we have reason to believe
    809  * that something changed.
    810  *
    811  * @return NULL on error
    812  */
    813 static struct WireStateHandle *
    814 get_wire_state (void)
    815 {
    816   struct WireStateHandle *old_wsh;
    817 
    818   old_wsh = wire_state;
    819   if ( (NULL == old_wsh) ||
    820        (old_wsh->wire_generation < wire_generation) )
    821   {
    822     struct WireStateHandle *wsh;
    823 
    824     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    825                 "Rebuilding wire, generation upgrade from %llu to %llu\n",
    826                 (unsigned long long) (NULL == old_wsh) ? 0LL :
    827                 old_wsh->wire_generation,
    828                 (unsigned long long) wire_generation);
    829     TEH_check_invariants ();
    830     wsh = build_wire_state ();
    831     wire_state = wsh;
    832     if (NULL != old_wsh)
    833       destroy_wire_state (old_wsh);
    834     TEH_check_invariants ();
    835     return wsh;
    836   }
    837   return old_wsh;
    838 }
    839 
    840 
    841 const struct TALER_WireFeeSet *
    842 TEH_wire_fees_by_time (
    843   struct GNUNET_TIME_Timestamp ts,
    844   const char *method)
    845 {
    846   struct WireStateHandle *wsh = get_wire_state ();
    847 
    848   for (struct WireFeeSet *wfs = wsh->wfs_head;
    849        NULL != wfs;
    850        wfs = wfs->next)
    851   {
    852     if (0 != strcmp (method,
    853                      wfs->method))
    854       continue;
    855     if ( (GNUNET_TIME_timestamp_cmp (wfs->start_date,
    856                                      >,
    857                                      ts)) ||
    858          (GNUNET_TIME_timestamp_cmp (ts,
    859                                      >=,
    860                                      wfs->end_date)) )
    861       continue;
    862     return &wfs->fees;
    863   }
    864   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    865               "No wire fees for method `%s' at %s configured\n",
    866               method,
    867               GNUNET_TIME_timestamp2s (ts));
    868   return NULL;
    869 }
    870 
    871 
    872 /**
    873  * Function called to forcefully resume suspended keys requests.
    874  *
    875  * @param cls unused, NULL
    876  */
    877 static void
    878 keys_timeout_cb (void *cls)
    879 {
    880   struct SuspendedKeysRequests *skr;
    881 
    882   (void) cls;
    883   keys_tt = NULL;
    884   while (NULL != (skr = skr_head))
    885   {
    886     if (GNUNET_TIME_absolute_is_future (skr->timeout))
    887       break;
    888     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    889                 "Resuming /keys request due to timeout\n");
    890     GNUNET_CONTAINER_DLL_remove (skr_head,
    891                                  skr_tail,
    892                                  skr);
    893     skr_size--;
    894     MHD_resume_connection (skr->connection);
    895     TALER_MHD_daemon_trigger ();
    896     GNUNET_free (skr);
    897   }
    898   if (NULL == skr)
    899     return;
    900   keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout,
    901                                      &keys_timeout_cb,
    902                                      NULL);
    903 }
    904 
    905 
    906 /**
    907  * Suspend /keys request while we (hopefully) are waiting to be
    908  * provisioned with key material.
    909  *
    910  * @param[in] connection to suspend
    911  */
    912 static enum MHD_Result
    913 suspend_request (struct MHD_Connection *connection)
    914 {
    915   struct SuspendedKeysRequests *skr;
    916 
    917   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    918               "Suspending /keys request until key material changes\n");
    919   if (terminating)
    920   {
    921     return TALER_MHD_reply_with_error (connection,
    922                                        MHD_HTTP_SERVICE_UNAVAILABLE,
    923                                        TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
    924                                        "Exchange terminating");
    925   }
    926   skr = GNUNET_new (struct SuspendedKeysRequests);
    927   skr->connection = connection;
    928   MHD_suspend_connection (connection);
    929   GNUNET_CONTAINER_DLL_insert (skr_head,
    930                                skr_tail,
    931                                skr);
    932   skr_size++;
    933   skr->timeout = GNUNET_TIME_relative_to_absolute (KEYS_TIMEOUT);
    934   if (NULL == keys_tt)
    935   {
    936     keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout,
    937                                        &keys_timeout_cb,
    938                                        NULL);
    939   }
    940   while (skr_size > SKR_LIMIT)
    941   {
    942     skr = skr_tail;
    943     GNUNET_CONTAINER_DLL_remove (skr_head,
    944                                  skr_tail,
    945                                  skr);
    946     skr_size--;
    947     MHD_resume_connection (skr->connection);
    948     TALER_MHD_daemon_trigger ();
    949     GNUNET_free (skr);
    950   }
    951   return MHD_YES;
    952 }
    953 
    954 
    955 /**
    956  * Called on each denomination key. Checks that the key still works.
    957  *
    958  * @param cls NULL
    959  * @param hc denomination hash (unused)
    960  * @param value a `struct TEH_DenominationKey`
    961  * @return #GNUNET_OK
    962  */
    963 static enum GNUNET_GenericReturnValue
    964 check_dk (void *cls,
    965           const struct GNUNET_HashCode *hc,
    966           void *value)
    967 {
    968   struct TEH_DenominationKey *dk = value;
    969 
    970   (void) cls;
    971   (void) hc;
    972   switch (dk->denom_pub.bsign_pub_key->cipher)
    973   {
    974   case GNUNET_CRYPTO_BSA_INVALID:
    975     break;
    976   case GNUNET_CRYPTO_BSA_RSA:
    977     GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check (
    978                      dk->denom_pub.bsign_pub_key->details.rsa_public_key));
    979     return GNUNET_OK;
    980   case GNUNET_CRYPTO_BSA_CS:
    981     /* nothing to do for GNUNET_CRYPTO_BSA_CS */
    982     return GNUNET_OK;
    983   }
    984   GNUNET_assert (0);
    985   return GNUNET_SYSERR;
    986 }
    987 
    988 
    989 void
    990 TEH_check_invariants ()
    991 {
    992   struct TEH_KeyStateHandle *ksh;
    993 
    994   if (0 == TEH_check_invariants_flag)
    995     return;
    996   ksh = TEH_keys_get_state ();
    997   if (NULL == ksh)
    998     return;
    999   GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
   1000                                          &check_dk,
   1001                                          NULL);
   1002 }
   1003 
   1004 
   1005 void
   1006 TEH_resume_keys_requests (bool do_shutdown)
   1007 {
   1008   struct SuspendedKeysRequests *skr;
   1009 
   1010   if (do_shutdown)
   1011     terminating = true;
   1012   while (NULL != (skr = skr_head))
   1013   {
   1014     GNUNET_CONTAINER_DLL_remove (skr_head,
   1015                                  skr_tail,
   1016                                  skr);
   1017     skr_size--;
   1018     MHD_resume_connection (skr->connection);
   1019     TALER_MHD_daemon_trigger ();
   1020     GNUNET_free (skr);
   1021   }
   1022   GNUNET_assert (0 == skr_size);
   1023 }
   1024 
   1025 
   1026 /**
   1027  * Clear memory for responses to "/keys" in @a ksh.
   1028  *
   1029  * @param[in,out] ksh key state to update
   1030  */
   1031 static void
   1032 clear_response_cache (struct TEH_KeyStateHandle *ksh)
   1033 {
   1034   for (unsigned int i = 0; i<ksh->krd_array_length; i++)
   1035   {
   1036     struct KeysResponseData *krd = &ksh->krd_array[i];
   1037 
   1038     MHD_destroy_response (krd->response_compressed);
   1039     MHD_destroy_response (krd->response_uncompressed);
   1040     GNUNET_free (krd->etag);
   1041   }
   1042   GNUNET_array_grow (ksh->krd_array,
   1043                      ksh->krd_array_length,
   1044                      0);
   1045 }
   1046 
   1047 
   1048 /**
   1049  * Free denomination key data.
   1050  *
   1051  * @param cls a `struct TEH_KeyStateHandle`, unused
   1052  * @param h_denom_pub hash of the denomination public key, unused
   1053  * @param value a `struct TEH_DenominationKey` to free
   1054  * @return #GNUNET_OK (continue to iterate)
   1055  */
   1056 static enum GNUNET_GenericReturnValue
   1057 clear_denomination_cb (void *cls,
   1058                        const struct GNUNET_HashCode *h_denom_pub,
   1059                        void *value)
   1060 {
   1061   struct TEH_DenominationKey *dk = value;
   1062   struct TEH_AuditorSignature *as;
   1063 
   1064   (void) cls;
   1065   (void) h_denom_pub;
   1066   TALER_denom_pub_free (&dk->denom_pub);
   1067   while (NULL != (as = dk->as_head))
   1068   {
   1069     GNUNET_CONTAINER_DLL_remove (dk->as_head,
   1070                                  dk->as_tail,
   1071                                  as);
   1072     GNUNET_free (as);
   1073   }
   1074   GNUNET_free (dk);
   1075   return GNUNET_OK;
   1076 }
   1077 
   1078 
   1079 /**
   1080  * Free denomination key data.
   1081  *
   1082  * @param cls a `struct TEH_KeyStateHandle`, unused
   1083  * @param pid the online signing key (type-disguised), unused
   1084  * @param value a `struct SigningKey` to free
   1085  * @return #GNUNET_OK (continue to iterate)
   1086  */
   1087 static enum GNUNET_GenericReturnValue
   1088 clear_signkey_cb (void *cls,
   1089                   const struct GNUNET_PeerIdentity *pid,
   1090                   void *value)
   1091 {
   1092   struct SigningKey *sk = value;
   1093 
   1094   (void) cls;
   1095   (void) pid;
   1096   GNUNET_free (sk);
   1097   return GNUNET_OK;
   1098 }
   1099 
   1100 
   1101 /**
   1102  * Free resources associated with @a cls, possibly excluding
   1103  * the helper data.
   1104  *
   1105  * @param[in] ksh key state to release
   1106  */
   1107 static void
   1108 destroy_key_state (struct TEH_KeyStateHandle *ksh)
   1109 {
   1110   struct TEH_GlobalFee *gf;
   1111 
   1112   clear_response_cache (ksh);
   1113   while (NULL != (gf = ksh->gf_head))
   1114   {
   1115     GNUNET_CONTAINER_DLL_remove (ksh->gf_head,
   1116                                  ksh->gf_tail,
   1117                                  gf);
   1118     GNUNET_free (gf);
   1119   }
   1120   GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
   1121                                          &clear_denomination_cb,
   1122                                          ksh);
   1123   GNUNET_CONTAINER_multihashmap_destroy (ksh->denomkey_map);
   1124   GNUNET_CONTAINER_multihashmap32_destroy (ksh->denomserial_map);
   1125   GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map,
   1126                                          &clear_signkey_cb,
   1127                                          ksh);
   1128   GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map);
   1129   json_decref (ksh->auditors);
   1130   ksh->auditors = NULL;
   1131   json_decref (ksh->global_fees);
   1132   ksh->global_fees = NULL;
   1133   if (NULL != ksh->management_keys_reply)
   1134   {
   1135     json_decref (ksh->management_keys_reply);
   1136     ksh->management_keys_reply = NULL;
   1137   }
   1138   GNUNET_free (ksh);
   1139 }
   1140 
   1141 
   1142 /**
   1143  * Function called whenever another exchange process has updated
   1144  * the keys data in the database.
   1145  *
   1146  * @param cls NULL
   1147  * @param extra unused
   1148  * @param extra_size number of bytes in @a extra unused
   1149  */
   1150 static void
   1151 keys_update_event_cb (void *cls,
   1152                       const void *extra,
   1153                       size_t extra_size)
   1154 {
   1155   (void) cls;
   1156   (void) extra;
   1157   (void) extra_size;
   1158   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1159               "Received /keys update event\n");
   1160   TEH_check_invariants ();
   1161   key_generation++;
   1162   TEH_resume_keys_requests (false);
   1163   TEH_check_invariants ();
   1164 }
   1165 
   1166 
   1167 enum GNUNET_GenericReturnValue
   1168 TEH_keys_init ()
   1169 {
   1170   if (GNUNET_OK !=
   1171       TEH_SECMOD_setup_key_helpers ())
   1172   {
   1173     TEH_SECMOD_destroy_key_helpers ();
   1174     return GNUNET_SYSERR;
   1175   }
   1176   if (GNUNET_OK !=
   1177       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
   1178                                            "exchange",
   1179                                            "SIGNKEY_LEGAL_DURATION",
   1180                                            &signkey_legal_duration))
   1181   {
   1182     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1183                                "exchange",
   1184                                "SIGNKEY_LEGAL_DURATION");
   1185     return GNUNET_SYSERR;
   1186   }
   1187   if (GNUNET_OK !=
   1188       GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   1189                                              "exchange",
   1190                                              "ASSET_TYPE",
   1191                                              &asset_type))
   1192   {
   1193     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   1194                                "exchange",
   1195                                "ASSET_TYPE");
   1196     asset_type = GNUNET_strdup ("fiat");
   1197   }
   1198   {
   1199     struct GNUNET_DB_EventHeaderP es = {
   1200       .size = htons (sizeof (es)),
   1201       .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED),
   1202     };
   1203 
   1204     keys_eh = TALER_EXCHANGEDB_event_listen (TEH_pg,
   1205                                              GNUNET_TIME_UNIT_FOREVER_REL,
   1206                                              &es,
   1207                                              &keys_update_event_cb,
   1208                                              NULL);
   1209     if (NULL == keys_eh)
   1210     {
   1211       GNUNET_break (0);
   1212       return GNUNET_SYSERR;
   1213     }
   1214   }
   1215   {
   1216     struct GNUNET_DB_EventHeaderP es = {
   1217       .size = htons (sizeof (es)),
   1218       .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED),
   1219     };
   1220 
   1221     wire_eh = TALER_EXCHANGEDB_event_listen (TEH_pg,
   1222                                              GNUNET_TIME_UNIT_FOREVER_REL,
   1223                                              &es,
   1224                                              &wire_update_event_cb,
   1225                                              NULL);
   1226     if (NULL == wire_eh)
   1227     {
   1228       GNUNET_break (0);
   1229       return GNUNET_SYSERR;
   1230     }
   1231   }
   1232   return GNUNET_OK;
   1233 }
   1234 
   1235 
   1236 /**
   1237  * Fully clean up our state.
   1238  */
   1239 void
   1240 TEH_keys_finished ()
   1241 {
   1242   if (NULL != wire_state)
   1243   {
   1244     destroy_wire_state (wire_state);
   1245     wire_state = NULL;
   1246   }
   1247   if (NULL != wire_eh)
   1248   {
   1249     TALER_EXCHANGEDB_event_listen_cancel (TEH_pg,
   1250                                           wire_eh);
   1251     wire_eh = NULL;
   1252   }
   1253   if (NULL != keys_tt)
   1254   {
   1255     GNUNET_SCHEDULER_cancel (keys_tt);
   1256     keys_tt = NULL;
   1257   }
   1258   if (NULL != key_state)
   1259     destroy_key_state (key_state);
   1260   if (NULL != keys_eh)
   1261   {
   1262     TALER_EXCHANGEDB_event_listen_cancel (TEH_pg,
   1263                                           keys_eh);
   1264     keys_eh = NULL;
   1265   }
   1266   TEH_SECMOD_destroy_key_helpers ();
   1267 }
   1268 
   1269 
   1270 /**
   1271  * Function called with information about the exchange's denomination keys.
   1272  *
   1273  * @param ksh closure with a `struct TEH_KeyStateHandle *`
   1274  * @param denom_pub public key of the denomination
   1275  * @param h_denom_pub hash of @a denom_pub
   1276  * @param meta meta data information about the denomination type (value, expirations, fees)
   1277  * @param master_sig master signature affirming the validity of this denomination
   1278  * @param recoup_possible true if the key was revoked and clients can currently recoup
   1279  *        coins of this denomination
   1280  */
   1281 static void
   1282 denomination_info_cb (
   1283   struct TEH_KeyStateHandle *ksh,
   1284   const struct TALER_DenominationPublicKey *denom_pub,
   1285   const struct TALER_DenominationHashP *h_denom_pub,
   1286   const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta,
   1287   const struct TALER_MasterSignatureP *master_sig,
   1288   bool recoup_possible)
   1289 {
   1290   struct TEH_DenominationKey *dk;
   1291 
   1292   if (GNUNET_TIME_absolute_is_past (meta->expire_deposit.abs_time))
   1293   {
   1294     /* should have been filtered by DB query already! */
   1295     GNUNET_break (0);
   1296     return;
   1297   }
   1298   if (GNUNET_OK !=
   1299       TALER_exchange_offline_denom_validity_verify (
   1300         h_denom_pub,
   1301         meta->start,
   1302         meta->expire_withdraw,
   1303         meta->expire_deposit,
   1304         meta->expire_legal,
   1305         &meta->value,
   1306         &meta->fees,
   1307         &TEH_master_public_key,
   1308         master_sig))
   1309   {
   1310     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1311                 "Database has denomination with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
   1312     return;
   1313   }
   1314 
   1315   GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID !=
   1316                  denom_pub->bsign_pub_key->cipher);
   1317   if (GNUNET_TIME_absolute_is_zero (meta->start.abs_time) ||
   1318       GNUNET_TIME_absolute_is_zero (meta->expire_withdraw.abs_time) ||
   1319       GNUNET_TIME_absolute_is_zero (meta->expire_deposit.abs_time) ||
   1320       GNUNET_TIME_absolute_is_zero (meta->expire_legal.abs_time) )
   1321   {
   1322     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1323                 "Database contains invalid denomination key %s\n",
   1324                 GNUNET_h2s (&h_denom_pub->hash));
   1325     return;
   1326   }
   1327   dk = GNUNET_new (struct TEH_DenominationKey);
   1328   TALER_denom_pub_copy (&dk->denom_pub,
   1329                         denom_pub);
   1330   dk->h_denom_pub = *h_denom_pub;
   1331   dk->meta = *meta;
   1332   dk->master_sig = *master_sig;
   1333   dk->recoup_possible = recoup_possible;
   1334   dk->denom_pub.age_mask = meta->age_mask;
   1335 
   1336   GNUNET_assert (
   1337     GNUNET_OK ==
   1338     GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map,
   1339                                        &dk->h_denom_pub.hash,
   1340                                        dk,
   1341                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
   1342   {
   1343     uint32_t serial32 = (uint32_t) dk->meta.serial;
   1344 
   1345     GNUNET_assert (dk->meta.serial == (uint64_t) serial32);
   1346     GNUNET_assert (
   1347       GNUNET_OK ==
   1348       GNUNET_CONTAINER_multihashmap32_put (ksh->denomserial_map,
   1349                                            serial32,
   1350                                            dk,
   1351                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
   1352   }
   1353 }
   1354 
   1355 
   1356 /**
   1357  * Function called with information about the exchange's online signing keys.
   1358  *
   1359  * @param ksh closure with a `struct TEH_KeyStateHandle *`
   1360  * @param exchange_pub the public key
   1361  * @param meta meta data information about the denomination type (expirations)
   1362  * @param master_sig master signature affirming the validity of this denomination
   1363  */
   1364 static void
   1365 signkey_info_cb (
   1366   struct TEH_KeyStateHandle *ksh,
   1367   const struct TALER_ExchangePublicKeyP *exchange_pub,
   1368   const struct TALER_EXCHANGEDB_SignkeyMetaData *meta,
   1369   const struct TALER_MasterSignatureP *master_sig)
   1370 {
   1371   struct SigningKey *sk;
   1372   struct GNUNET_PeerIdentity pid;
   1373 
   1374   if (GNUNET_OK !=
   1375       TALER_exchange_offline_signkey_validity_verify (
   1376         exchange_pub,
   1377         meta->start,
   1378         meta->expire_sign,
   1379         meta->expire_legal,
   1380         &TEH_master_public_key,
   1381         master_sig))
   1382   {
   1383     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1384                 "Database has signing key with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
   1385     return;
   1386   }
   1387   sk = GNUNET_new (struct SigningKey);
   1388   sk->exchange_pub = *exchange_pub;
   1389   sk->meta = *meta;
   1390   sk->master_sig = *master_sig;
   1391   pid.public_key = exchange_pub->eddsa_pub;
   1392   GNUNET_assert (
   1393     GNUNET_OK ==
   1394     GNUNET_CONTAINER_multipeermap_put (ksh->signkey_map,
   1395                                        &pid,
   1396                                        sk,
   1397                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
   1398 }
   1399 
   1400 
   1401 /**
   1402  * Closure for #get_auditor_sigs.
   1403  */
   1404 struct GetAuditorSigsContext
   1405 {
   1406   /**
   1407    * Where to store the matching signatures.
   1408    */
   1409   json_t *denom_keys;
   1410 
   1411   /**
   1412    * Public key of the auditor to match against.
   1413    */
   1414   const struct TALER_AuditorPublicKeyP *auditor_pub;
   1415 };
   1416 
   1417 
   1418 /**
   1419  * Extract the auditor signatures matching the auditor's public
   1420  * key from the @a value and generate the respective JSON.
   1421  *
   1422  * @param cls a `struct GetAuditorSigsContext`
   1423  * @param h_denom_pub hash of the denomination public key
   1424  * @param value a `struct TEH_DenominationKey`
   1425  * @return #GNUNET_OK (continue to iterate)
   1426  */
   1427 static enum GNUNET_GenericReturnValue
   1428 get_auditor_sigs (void *cls,
   1429                   const struct GNUNET_HashCode *h_denom_pub,
   1430                   void *value)
   1431 {
   1432   struct GetAuditorSigsContext *ctx = cls;
   1433   struct TEH_DenominationKey *dk = value;
   1434 
   1435   for (struct TEH_AuditorSignature *as = dk->as_head;
   1436        NULL != as;
   1437        as = as->next)
   1438   {
   1439     if (0 !=
   1440         GNUNET_memcmp (ctx->auditor_pub,
   1441                        &as->apub))
   1442       continue;
   1443     GNUNET_break (0 ==
   1444                   json_array_append_new (
   1445                     ctx->denom_keys,
   1446                     GNUNET_JSON_PACK (
   1447                       GNUNET_JSON_pack_data_auto ("denom_pub_h",
   1448                                                   h_denom_pub),
   1449                       GNUNET_JSON_pack_data_auto ("auditor_sig",
   1450                                                   &as->asig))));
   1451   }
   1452   return GNUNET_OK;
   1453 }
   1454 
   1455 
   1456 /**
   1457  * Function called with information about the exchange's auditors.
   1458  *
   1459  * @param ksh closure with a `struct TEH_KeyStateHandle *`
   1460  * @param auditor_pub the public key of the auditor
   1461  * @param auditor_url URL of the REST API of the auditor
   1462  * @param auditor_name human readable official name of the auditor
   1463  */
   1464 static void
   1465 auditor_info_cb (
   1466   struct TEH_KeyStateHandle *ksh,
   1467   const struct TALER_AuditorPublicKeyP *auditor_pub,
   1468   const char *auditor_url,
   1469   const char *auditor_name)
   1470 {
   1471   struct GetAuditorSigsContext ctx;
   1472 
   1473   ctx.denom_keys = json_array ();
   1474   GNUNET_assert (NULL != ctx.denom_keys);
   1475   ctx.auditor_pub = auditor_pub;
   1476   GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
   1477                                          &get_auditor_sigs,
   1478                                          &ctx);
   1479   GNUNET_break (0 ==
   1480                 json_array_append_new (
   1481                   ksh->auditors,
   1482                   GNUNET_JSON_PACK (
   1483                     GNUNET_JSON_pack_string ("auditor_name",
   1484                                              auditor_name),
   1485                     GNUNET_JSON_pack_data_auto ("auditor_pub",
   1486                                                 auditor_pub),
   1487                     GNUNET_JSON_pack_string ("auditor_url",
   1488                                              auditor_url),
   1489                     GNUNET_JSON_pack_array_steal ("denomination_keys",
   1490                                                   ctx.denom_keys))));
   1491 }
   1492 
   1493 
   1494 /**
   1495  * Function called with information about the denominations
   1496  * audited by the exchange's auditors.
   1497  *
   1498  * @param ksh closure with a `struct TEH_KeyStateHandle *`
   1499  * @param auditor_pub the public key of an auditor
   1500  * @param h_denom_pub hash of a denomination key audited by this auditor
   1501  * @param auditor_sig signature from the auditor affirming this
   1502  */
   1503 static void
   1504 auditor_denom_cb (
   1505   struct TEH_KeyStateHandle *ksh,
   1506   const struct TALER_AuditorPublicKeyP *auditor_pub,
   1507   const struct TALER_DenominationHashP *h_denom_pub,
   1508   const struct TALER_AuditorSignatureP *auditor_sig)
   1509 {
   1510   struct TEH_DenominationKey *dk;
   1511   struct TEH_AuditorSignature *as;
   1512 
   1513   dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
   1514                                           &h_denom_pub->hash);
   1515   if (NULL == dk)
   1516   {
   1517     /* Odd, this should be impossible as per foreign key
   1518        constraint on 'auditor_denom_sigs'! Well, we can
   1519        safely continue anyway, so let's just log it. */
   1520     GNUNET_break (0);
   1521     return;
   1522   }
   1523   as = GNUNET_new (struct TEH_AuditorSignature);
   1524   as->asig = *auditor_sig;
   1525   as->apub = *auditor_pub;
   1526   GNUNET_CONTAINER_DLL_insert (dk->as_head,
   1527                                dk->as_tail,
   1528                                as);
   1529 }
   1530 
   1531 
   1532 /**
   1533  * Closure for #add_sign_key_cb.
   1534  */
   1535 struct SignKeyCtx
   1536 {
   1537   /**
   1538    * What is the current rotation frequency for signing keys. Updated.
   1539    */
   1540   struct GNUNET_TIME_Relative min_sk_frequency;
   1541 
   1542   /**
   1543    * JSON array of signing keys (being created).
   1544    */
   1545   json_t *signkeys;
   1546 };
   1547 
   1548 
   1549 /**
   1550  * Function called for all signing keys, used to build up the
   1551  * respective JSON response.
   1552  *
   1553  * @param cls a `struct SignKeyCtx *` with the array to append keys to
   1554  * @param pid the exchange public key (in type disguise)
   1555  * @param value a `struct SigningKey`
   1556  * @return #GNUNET_OK (continue to iterate)
   1557  */
   1558 static enum GNUNET_GenericReturnValue
   1559 add_sign_key_cb (void *cls,
   1560                  const struct GNUNET_PeerIdentity *pid,
   1561                  void *value)
   1562 {
   1563   struct SignKeyCtx *ctx = cls;
   1564   struct SigningKey *sk = value;
   1565 
   1566   (void) pid;
   1567   if (GNUNET_TIME_absolute_is_future (sk->meta.expire_sign.abs_time))
   1568   {
   1569     ctx->min_sk_frequency =
   1570       GNUNET_TIME_relative_min (ctx->min_sk_frequency,
   1571                                 GNUNET_TIME_absolute_get_difference (
   1572                                   sk->meta.start.abs_time,
   1573                                   sk->meta.expire_sign.abs_time));
   1574   }
   1575   GNUNET_assert (
   1576     0 ==
   1577     json_array_append_new (
   1578       ctx->signkeys,
   1579       GNUNET_JSON_PACK (
   1580         GNUNET_JSON_pack_timestamp ("stamp_start",
   1581                                     sk->meta.start),
   1582         GNUNET_JSON_pack_timestamp ("stamp_expire",
   1583                                     sk->meta.expire_sign),
   1584         GNUNET_JSON_pack_timestamp ("stamp_end",
   1585                                     sk->meta.expire_legal),
   1586         GNUNET_JSON_pack_data_auto ("master_sig",
   1587                                     &sk->master_sig),
   1588         GNUNET_JSON_pack_data_auto ("key",
   1589                                     &sk->exchange_pub))));
   1590   return GNUNET_OK;
   1591 }
   1592 
   1593 
   1594 /**
   1595  * Closure for #add_denom_key_cb.
   1596  */
   1597 struct DenomKeyCtx
   1598 {
   1599   /**
   1600    * Heap for sorting active denomination keys by start time.
   1601    */
   1602   struct GNUNET_CONTAINER_Heap *heap;
   1603 
   1604   /**
   1605    * JSON array of revoked denomination keys.
   1606    */
   1607   json_t *recoup;
   1608 
   1609   /**
   1610    * What is the minimum key rotation frequency of
   1611    * valid denomination keys?
   1612    */
   1613   struct GNUNET_TIME_Relative min_dk_frequency;
   1614 };
   1615 
   1616 
   1617 /**
   1618  * Function called for all denomination keys, used to build up the
   1619  * JSON list of *revoked* denomination keys and the
   1620  * heap of non-revoked denomination keys by timeout.
   1621  *
   1622  * @param cls a `struct DenomKeyCtx`
   1623  * @param h_denom_pub hash of the denomination key
   1624  * @param value a `struct TEH_DenominationKey`
   1625  * @return #GNUNET_OK (continue to iterate)
   1626  */
   1627 static enum GNUNET_GenericReturnValue
   1628 add_denom_key_cb (void *cls,
   1629                   const struct GNUNET_HashCode *h_denom_pub,
   1630                   void *value)
   1631 {
   1632   struct DenomKeyCtx *dkc = cls;
   1633   struct TEH_DenominationKey *dk = value;
   1634 
   1635   if (dk->recoup_possible)
   1636   {
   1637     GNUNET_assert (
   1638       0 ==
   1639       json_array_append_new (
   1640         dkc->recoup,
   1641         GNUNET_JSON_PACK (
   1642           GNUNET_JSON_pack_data_auto ("h_denom_pub",
   1643                                       h_denom_pub))));
   1644   }
   1645   else
   1646   {
   1647     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
   1648     {
   1649       dkc->min_dk_frequency =
   1650         GNUNET_TIME_relative_min (dkc->min_dk_frequency,
   1651                                   GNUNET_TIME_absolute_get_difference (
   1652                                     dk->meta.start.abs_time,
   1653                                     dk->meta.expire_withdraw.abs_time));
   1654     }
   1655     (void) GNUNET_CONTAINER_heap_insert (dkc->heap,
   1656                                          dk,
   1657                                          dk->meta.start.abs_time.abs_value_us);
   1658   }
   1659   return GNUNET_OK;
   1660 }
   1661 
   1662 
   1663 /**
   1664  * Add the headers we want to set for every /keys response.
   1665  *
   1666  * @param cls the key state to use
   1667  * @param[in,out] response the response to modify
   1668  */
   1669 static void
   1670 setup_general_response_headers (void *cls,
   1671                                 struct MHD_Response *response)
   1672 {
   1673   struct TEH_KeyStateHandle *ksh = cls;
   1674   char dat[128];
   1675 
   1676   TALER_MHD_add_global_headers (response,
   1677                                 true);
   1678   GNUNET_break (MHD_YES ==
   1679                 MHD_add_response_header (response,
   1680                                          MHD_HTTP_HEADER_CONTENT_TYPE,
   1681                                          "application/json"));
   1682   GNUNET_break (MHD_YES ==
   1683                 MHD_add_response_header (response,
   1684                                          MHD_HTTP_HEADER_CACHE_CONTROL,
   1685                                          "public,must-revalidate,max-age=86400")
   1686                 );
   1687   if (! GNUNET_TIME_relative_is_zero (ksh->rekey_frequency))
   1688   {
   1689     struct GNUNET_TIME_Relative r;
   1690     struct GNUNET_TIME_Absolute a;
   1691     struct GNUNET_TIME_Timestamp km;
   1692     struct GNUNET_TIME_Timestamp m;
   1693     struct GNUNET_TIME_Timestamp we;
   1694 
   1695     r = GNUNET_TIME_relative_min (TEH_max_keys_caching,
   1696                                   ksh->rekey_frequency);
   1697     a = GNUNET_TIME_relative_to_absolute (r);
   1698     /* Round up to next full day to ensure the expiration
   1699        time does not become a fingerprint! */
   1700     a = GNUNET_TIME_absolute_round_down (a,
   1701                                          GNUNET_TIME_UNIT_DAYS);
   1702     a = GNUNET_TIME_absolute_add (a,
   1703                                   GNUNET_TIME_UNIT_DAYS);
   1704     km = GNUNET_TIME_absolute_to_timestamp (a);
   1705     we = GNUNET_TIME_absolute_to_timestamp (wire_state->cache_expiration);
   1706     m = GNUNET_TIME_timestamp_min (we,
   1707                                    km);
   1708     TALER_MHD_get_date_string (m.abs_time,
   1709                                dat);
   1710     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1711                 "Setting /keys 'Expires' header to '%s' (rekey frequency is %s)\n",
   1712                 dat,
   1713                 GNUNET_TIME_relative2s (ksh->rekey_frequency,
   1714                                         false));
   1715     GNUNET_break (MHD_YES ==
   1716                   MHD_add_response_header (response,
   1717                                            MHD_HTTP_HEADER_EXPIRES,
   1718                                            dat));
   1719     ksh->signature_expires
   1720       = GNUNET_TIME_timestamp_min (m,
   1721                                    ksh->signature_expires);
   1722   }
   1723   /* Set cache control headers: our response varies depending on these headers */
   1724   GNUNET_break (MHD_YES ==
   1725                 MHD_add_response_header (response,
   1726                                          MHD_HTTP_HEADER_VARY,
   1727                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
   1728 }
   1729 
   1730 
   1731 /**
   1732  * Initialize @a krd using the given values for @a signkeys,
   1733  * @a recoup and @a denoms.
   1734  *
   1735  * @param[in,out] ksh key state handle we build @a krd for
   1736  * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms
   1737  * @param last_cherry_pick_date timestamp to use
   1738  * @param[in,out] signkeys list of sign keys to return
   1739  * @param[in,out] recoup list of revoked keys to return
   1740  * @param[in,out] grouped_denominations list of grouped denominations to return
   1741  * @return #GNUNET_OK on success
   1742  */
   1743 static enum GNUNET_GenericReturnValue
   1744 create_krd (struct TEH_KeyStateHandle *ksh,
   1745             const struct GNUNET_HashCode *denom_keys_hash,
   1746             struct GNUNET_TIME_Timestamp last_cherry_pick_date,
   1747             json_t *signkeys,
   1748             json_t *recoup,
   1749             json_t *grouped_denominations)
   1750 {
   1751   struct KeysResponseData krd;
   1752   struct TALER_ExchangePublicKeyP exchange_pub;
   1753   struct TALER_ExchangeSignatureP exchange_sig;
   1754   struct WireStateHandle *wsh;
   1755   json_t *keys;
   1756 
   1757   wsh = get_wire_state ();
   1758   if (! wsh->ready)
   1759   {
   1760     GNUNET_break (0);
   1761     return GNUNET_SYSERR;
   1762   }
   1763   GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
   1764                    last_cherry_pick_date.abs_time));
   1765   GNUNET_assert (NULL != signkeys);
   1766   GNUNET_assert (NULL != recoup);
   1767   GNUNET_assert (NULL != grouped_denominations);
   1768   GNUNET_assert (NULL != ksh->auditors);
   1769   GNUNET_assert (NULL != TEH_currency);
   1770   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1771               "Creating /keys at cherry pick date %s\n",
   1772               GNUNET_TIME_timestamp2s (last_cherry_pick_date));
   1773 
   1774   /* Sign hash over master signatures of all denomination keys until this time
   1775      (in reverse order). */
   1776   {
   1777     enum TALER_ErrorCode ec;
   1778 
   1779     if (TALER_EC_NONE !=
   1780         (ec =
   1781            TALER_exchange_online_key_set_sign (
   1782              &TEH_keys_exchange_sign2_,
   1783              ksh,
   1784              last_cherry_pick_date,
   1785              denom_keys_hash,
   1786              &exchange_pub,
   1787              &exchange_sig)))
   1788     {
   1789       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1790                   "Could not create key response data: cannot sign (%s)\n",
   1791                   TALER_ErrorCode_get_hint (ec));
   1792       return GNUNET_SYSERR;
   1793     }
   1794   }
   1795 
   1796   {
   1797     const struct SigningKey *sk;
   1798 
   1799     sk = GNUNET_CONTAINER_multipeermap_get (
   1800       ksh->signkey_map,
   1801       (const struct GNUNET_PeerIdentity *) &exchange_pub);
   1802     GNUNET_assert (NULL != sk);
   1803     ksh->signature_expires = GNUNET_TIME_timestamp_min (sk->meta.expire_sign,
   1804                                                         ksh->signature_expires);
   1805   }
   1806 
   1807   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1808               "Build /keys data with %u wire accounts\n",
   1809               (unsigned int) json_array_size (
   1810                 json_object_get (wsh->json_reply,
   1811                                  "accounts")));
   1812 
   1813   keys = GNUNET_JSON_PACK (
   1814     GNUNET_JSON_pack_string ("version",
   1815                              EXCHANGE_PROTOCOL_VERSION),
   1816     GNUNET_JSON_pack_string ("base_url",
   1817                              TEH_base_url),
   1818     GNUNET_JSON_pack_string ("currency",
   1819                              TEH_currency),
   1820     GNUNET_JSON_pack_allow_null (
   1821       GNUNET_JSON_pack_string (
   1822         "bank_compliance_language",
   1823         TEH_bank_compliance_language)),
   1824     GNUNET_JSON_pack_object_steal (
   1825       "currency_specification",
   1826       TALER_JSON_currency_specs_to_json (TEH_cspec)),
   1827     GNUNET_JSON_pack_array_incref (
   1828       "hard_limits",
   1829       TEH_hard_limits),
   1830     GNUNET_JSON_pack_array_incref (
   1831       "zero_limits",
   1832       TEH_zero_limits),
   1833     TALER_JSON_pack_amount ("stefan_abs",
   1834                             &TEH_stefan_abs),
   1835     TALER_JSON_pack_amount ("stefan_log",
   1836                             &TEH_stefan_log),
   1837     GNUNET_JSON_pack_double ("stefan_lin",
   1838                              (double) TEH_stefan_lin),
   1839     GNUNET_JSON_pack_string ("asset_type",
   1840                              asset_type),
   1841     GNUNET_JSON_pack_bool ("kyc_enabled",
   1842                            GNUNET_YES == TEH_enable_kyc),
   1843     GNUNET_JSON_pack_bool ("kyc_swap_tos_acceptance",
   1844                            GNUNET_YES == TEH_kyc_swap_tos_acceptance),
   1845     GNUNET_JSON_pack_bool ("disable_direct_deposit",
   1846                            GNUNET_YES == TEH_disable_direct_deposit),
   1847     GNUNET_JSON_pack_bool ("rewards_allowed",
   1848                            false),
   1849     GNUNET_JSON_pack_data_auto ("master_public_key",
   1850                                 &TEH_master_public_key),
   1851     GNUNET_JSON_pack_time_rel ("reserve_closing_delay",
   1852                                TEH_reserve_closing_delay),
   1853     GNUNET_JSON_pack_time_rel ("default_p2p_push_expiration",
   1854                                TEH_default_p2p_expiration),
   1855     GNUNET_JSON_pack_array_incref ("signkeys",
   1856                                    signkeys),
   1857     GNUNET_JSON_pack_array_incref ("recoup",
   1858                                    recoup),
   1859     GNUNET_JSON_pack_array_incref ("wads",
   1860                                    json_object_get (wsh->json_reply,
   1861                                                     "wads")),
   1862     GNUNET_JSON_pack_array_incref ("accounts",
   1863                                    json_object_get (wsh->json_reply,
   1864                                                     "accounts")),
   1865     GNUNET_JSON_pack_object_incref ("wire_fees",
   1866                                     json_object_get (wsh->json_reply,
   1867                                                      "fees")),
   1868     GNUNET_JSON_pack_array_incref ("denominations",
   1869                                    grouped_denominations),
   1870     GNUNET_JSON_pack_array_incref ("auditors",
   1871                                    ksh->auditors),
   1872     GNUNET_JSON_pack_array_incref ("global_fees",
   1873                                    ksh->global_fees),
   1874     GNUNET_JSON_pack_timestamp ("list_issue_date",
   1875                                 last_cherry_pick_date),
   1876     GNUNET_JSON_pack_allow_null (
   1877       GNUNET_JSON_pack_array_steal (
   1878         "wallet_balance_limit_without_kyc",
   1879         TALER_KYCLOGIC_get_wallet_thresholds ())),
   1880     GNUNET_JSON_pack_allow_null (
   1881       GNUNET_JSON_pack_string ("shopping_url",
   1882                                TEH_shopping_url)),
   1883     TALER_JSON_pack_amount ("tiny_amount",
   1884                             &TEH_tiny_amount),
   1885     GNUNET_JSON_pack_data_auto ("exchange_pub",
   1886                                 &exchange_pub),
   1887     GNUNET_JSON_pack_data_auto ("exchange_sig",
   1888                                 &exchange_sig));
   1889   GNUNET_assert (NULL != keys);
   1890 
   1891   {
   1892     char *keys_json;
   1893     void *keys_jsonz;
   1894     size_t keys_jsonz_size;
   1895     int comp;
   1896     char etag[sizeof (struct GNUNET_HashCode) * 2];
   1897 
   1898     /* Convert /keys response to UTF8-String */
   1899     keys_json = json_dumps (keys,
   1900                             JSON_INDENT (2));
   1901     json_decref (keys);
   1902     GNUNET_assert (NULL != keys_json);
   1903 
   1904     /* Keep copy for later compression... */
   1905     keys_jsonz = GNUNET_strdup (keys_json);
   1906     keys_jsonz_size = strlen (keys_json);
   1907 
   1908     /* hash to compute etag */
   1909     {
   1910       struct GNUNET_HashCode ehash;
   1911       char *end;
   1912 
   1913       GNUNET_CRYPTO_hash (keys_jsonz,
   1914                           keys_jsonz_size,
   1915                           &ehash);
   1916       end = GNUNET_STRINGS_data_to_string (&ehash,
   1917                                            sizeof (ehash),
   1918                                            etag,
   1919                                            sizeof (etag));
   1920       *end = '\0';
   1921     }
   1922 
   1923     /* Create uncompressed response */
   1924     krd.response_uncompressed
   1925       = MHD_create_response_from_buffer (keys_jsonz_size,
   1926                                          keys_json,
   1927                                          MHD_RESPMEM_MUST_FREE);
   1928     GNUNET_assert (NULL != krd.response_uncompressed);
   1929     setup_general_response_headers (ksh,
   1930                                     krd.response_uncompressed);
   1931     /* Information is always public, revalidate after 1 day */
   1932     GNUNET_break (MHD_YES ==
   1933                   MHD_add_response_header (krd.response_uncompressed,
   1934                                            MHD_HTTP_HEADER_ETAG,
   1935                                            etag));
   1936     /* Also compute compressed version of /keys response */
   1937     comp = TALER_MHD_body_compress (&keys_jsonz,
   1938                                     &keys_jsonz_size);
   1939     krd.response_compressed
   1940       = MHD_create_response_from_buffer (keys_jsonz_size,
   1941                                          keys_jsonz,
   1942                                          MHD_RESPMEM_MUST_FREE);
   1943     GNUNET_assert (NULL != krd.response_compressed);
   1944     /* If the response is actually compressed, set the
   1945        respective header. */
   1946     GNUNET_assert ( (MHD_YES != comp) ||
   1947                     (MHD_YES ==
   1948                      MHD_add_response_header (krd.response_compressed,
   1949                                               MHD_HTTP_HEADER_CONTENT_ENCODING,
   1950                                               "deflate")) );
   1951     setup_general_response_headers (ksh,
   1952                                     krd.response_compressed);
   1953     /* Information is always public, revalidate after 1 day */
   1954     GNUNET_break (MHD_YES ==
   1955                   MHD_add_response_header (krd.response_compressed,
   1956                                            MHD_HTTP_HEADER_ETAG,
   1957                                            etag));
   1958     krd.etag = GNUNET_strdup (etag);
   1959   }
   1960   krd.cherry_pick_date = last_cherry_pick_date;
   1961   GNUNET_array_append (ksh->krd_array,
   1962                        ksh->krd_array_length,
   1963                        krd);
   1964   return GNUNET_OK;
   1965 }
   1966 
   1967 
   1968 /**
   1969  * Element in the `struct SignatureContext` array.
   1970  */
   1971 struct SignatureElement
   1972 {
   1973 
   1974   /**
   1975    * Offset of the denomination in the group array,
   1976    * for sorting (2nd rank, ascending).
   1977    */
   1978   unsigned int offset;
   1979 
   1980   /**
   1981    * Offset of the group in the denominations array,
   1982    * for sorting (2nd rank, ascending).
   1983    */
   1984   unsigned int group_offset;
   1985 
   1986   /**
   1987    * Pointer to actual master signature to hash over.
   1988    */
   1989   struct TALER_MasterSignatureP master_sig;
   1990 };
   1991 
   1992 /**
   1993  * Context for collecting the array of master signatures
   1994  * needed to verify the exchange_sig online signature.
   1995  */
   1996 struct SignatureContext
   1997 {
   1998   /**
   1999    * Array of signatures to hash over.
   2000    */
   2001   struct SignatureElement *elements;
   2002 
   2003   /**
   2004    * Write offset in the @e elements array.
   2005    */
   2006   unsigned int elements_pos;
   2007 
   2008   /**
   2009    * Allocated space for @e elements.
   2010    */
   2011   unsigned int elements_size;
   2012 };
   2013 
   2014 
   2015 /**
   2016  * Determine order to sort two elements by before
   2017  * we hash the master signatures.  Used for
   2018  * sorting with qsort().
   2019  *
   2020  * @param a pointer to a `struct SignatureElement`
   2021  * @param b pointer to a `struct SignatureElement`
   2022  * @return 0 if equal, -1 if a < b, 1 if a > b.
   2023  */
   2024 static int
   2025 signature_context_sort_cb (const void *a,
   2026                            const void *b)
   2027 {
   2028   const struct SignatureElement *sa = a;
   2029   const struct SignatureElement *sb = b;
   2030 
   2031   if (sa->group_offset < sb->group_offset)
   2032     return -1;
   2033   if (sa->group_offset > sb->group_offset)
   2034     return 1;
   2035   if (sa->offset < sb->offset)
   2036     return -1;
   2037   if (sa->offset > sb->offset)
   2038     return 1;
   2039   /* We should never have two disjoint elements
   2040      with same time and offset */
   2041   GNUNET_assert (sa == sb);
   2042   return 0;
   2043 }
   2044 
   2045 
   2046 /**
   2047  * Append a @a master_sig to the @a sig_ctx using the
   2048  * given attributes for (later) sorting.
   2049  *
   2050  * @param[in,out] sig_ctx signature context to update
   2051  * @param group_offset offset for the group
   2052  * @param offset offset for the entry
   2053  * @param master_sig master signature for the entry
   2054  */
   2055 static void
   2056 append_signature (struct SignatureContext *sig_ctx,
   2057                   unsigned int group_offset,
   2058                   unsigned int offset,
   2059                   const struct TALER_MasterSignatureP *master_sig)
   2060 {
   2061   struct SignatureElement *element;
   2062   unsigned int new_size;
   2063 
   2064   if (sig_ctx->elements_pos == sig_ctx->elements_size)
   2065   {
   2066     if (0 == sig_ctx->elements_size)
   2067       new_size = 1024;
   2068     else
   2069       new_size = sig_ctx->elements_size * 2;
   2070     GNUNET_array_grow (sig_ctx->elements,
   2071                        sig_ctx->elements_size,
   2072                        new_size);
   2073   }
   2074   element = &sig_ctx->elements[sig_ctx->elements_pos++];
   2075   element->offset = offset;
   2076   element->group_offset = group_offset;
   2077   element->master_sig = *master_sig;
   2078 }
   2079 
   2080 
   2081 /**
   2082  * GroupData is the value we store for each group meta-data
   2083  */
   2084 struct GroupData
   2085 {
   2086   /**
   2087    * The json blob with the group meta-data and list of denominations
   2088    */
   2089   json_t *json;
   2090 
   2091   /**
   2092    * List of denominations for the group,
   2093    * included in @e json, do not free separately!
   2094    */
   2095   json_t *list;
   2096 
   2097   /**
   2098    * Offset of the group in the final array.
   2099    */
   2100   unsigned int group_off;
   2101 
   2102 };
   2103 
   2104 
   2105 /**
   2106  * Helper function called to clean up the group data
   2107  * in the denominations_by_group below.
   2108  *
   2109  * @param cls unused
   2110  * @param key unused
   2111  * @param value a `struct GroupData` to free
   2112  * @return #GNUNET_OK
   2113  */
   2114 static int
   2115 free_group (void *cls,
   2116             const struct GNUNET_HashCode *key,
   2117             void *value)
   2118 {
   2119   struct GroupData *gd = value;
   2120 
   2121   (void) cls;
   2122   (void) key;
   2123   GNUNET_free (gd);
   2124   return GNUNET_OK;
   2125 }
   2126 
   2127 
   2128 static void
   2129 compute_msig_hash (struct SignatureContext *sig_ctx,
   2130                    struct GNUNET_HashCode *hc)
   2131 {
   2132   struct GNUNET_HashContext *hash_context;
   2133 
   2134   hash_context = GNUNET_CRYPTO_hash_context_start ();
   2135   qsort (sig_ctx->elements,
   2136          sig_ctx->elements_pos,
   2137          sizeof (struct SignatureElement),
   2138          &signature_context_sort_cb);
   2139   for (unsigned int i = 0; i<sig_ctx->elements_pos; i++)
   2140   {
   2141     struct SignatureElement *element = &sig_ctx->elements[i];
   2142 
   2143     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2144                 "Adding %u,%u,%s\n",
   2145                 element->group_offset,
   2146                 element->offset,
   2147                 TALER_B2S (&element->master_sig));
   2148     GNUNET_CRYPTO_hash_context_read (hash_context,
   2149                                      &element->master_sig,
   2150                                      sizeof (element->master_sig));
   2151   }
   2152   GNUNET_CRYPTO_hash_context_finish (hash_context,
   2153                                      hc);
   2154 }
   2155 
   2156 
   2157 /**
   2158  * Update the "/keys" responses in @a ksh, computing the detailed replies.
   2159  *
   2160  * This function is to recompute all (including cherry-picked) responses we
   2161  * might want to return, based on the state already in @a ksh.
   2162  *
   2163  * @param[in,out] ksh state handle to update
   2164  * @return #GNUNET_OK on success
   2165  */
   2166 static enum GNUNET_GenericReturnValue
   2167 finish_keys_response (struct TEH_KeyStateHandle *ksh)
   2168 {
   2169   enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
   2170   json_t *recoup;
   2171   struct SignKeyCtx sctx = {
   2172     .min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL
   2173   };
   2174   json_t *grouped_denominations = NULL;
   2175   struct GNUNET_TIME_Timestamp last_cherry_pick_date;
   2176   struct GNUNET_CONTAINER_Heap *heap;
   2177   struct SignatureContext sig_ctx = { 0 };
   2178   /* Remember if we have any denomination with age restriction */
   2179   bool has_age_restricted_denomination = false;
   2180   struct WireStateHandle *wsh;
   2181 
   2182   wsh = get_wire_state ();
   2183   if (! wsh->ready)
   2184   {
   2185     GNUNET_break (0);
   2186     return GNUNET_SYSERR;
   2187   }
   2188   if (0 ==
   2189       json_array_size (json_object_get (wsh->json_reply,
   2190                                         "accounts")) )
   2191   {
   2192     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2193                 "No wire accounts available. Refusing to generate /keys response.\n");
   2194     return GNUNET_NO;
   2195   }
   2196   sctx.signkeys = json_array ();
   2197   GNUNET_assert (NULL != sctx.signkeys);
   2198   recoup = json_array ();
   2199   GNUNET_assert (NULL != recoup);
   2200   grouped_denominations = json_array ();
   2201   GNUNET_assert (NULL != grouped_denominations);
   2202 
   2203   GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map,
   2204                                          &add_sign_key_cb,
   2205                                          &sctx);
   2206   if (0 == json_array_size (sctx.signkeys))
   2207   {
   2208     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2209                 "No online signing keys available. Refusing to generate /keys response.\n");
   2210     ret = GNUNET_NO;
   2211     goto CLEANUP;
   2212   }
   2213   heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
   2214   {
   2215     struct DenomKeyCtx dkc = {
   2216       .recoup = recoup,
   2217       .heap = heap,
   2218       .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL,
   2219     };
   2220 
   2221     GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
   2222                                            &add_denom_key_cb,
   2223                                            &dkc);
   2224     ksh->rekey_frequency
   2225       = GNUNET_TIME_relative_min (dkc.min_dk_frequency,
   2226                                   sctx.min_sk_frequency);
   2227   }
   2228 
   2229   last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS;
   2230 
   2231   {
   2232     struct TEH_DenominationKey *dk;
   2233     struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group;
   2234 
   2235     denominations_by_group =
   2236       GNUNET_CONTAINER_multihashmap_create (1024,
   2237                                             GNUNET_NO /* NO, because keys are only on the stack */
   2238                                             );
   2239     /* heap = max heap, sorted by start time */
   2240     while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap)))
   2241     {
   2242       if (GNUNET_TIME_timestamp_cmp (last_cherry_pick_date,
   2243                                      !=,
   2244                                      dk->meta.start) &&
   2245           (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) )
   2246       {
   2247         /*
   2248          * This is not the first entry in the heap (because last_cherry_pick_date !=
   2249          * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different
   2250          * start time.  Therefore, we create a new entry in ksh.
   2251          */
   2252         struct GNUNET_HashCode hc;
   2253 
   2254         compute_msig_hash (&sig_ctx,
   2255                            &hc);
   2256         if (GNUNET_OK !=
   2257             create_krd (ksh,
   2258                         &hc,
   2259                         last_cherry_pick_date,
   2260                         sctx.signkeys,
   2261                         recoup,
   2262                         grouped_denominations))
   2263         {
   2264           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2265                       "Failed to generate key response data for %s\n",
   2266                       GNUNET_TIME_timestamp2s (last_cherry_pick_date));
   2267           /* drain heap before destroying it */
   2268           while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap)))
   2269             /* intentionally empty */;
   2270           GNUNET_CONTAINER_heap_destroy (heap);
   2271           goto CLEANUP;
   2272         }
   2273       }
   2274 
   2275       last_cherry_pick_date = dk->meta.start;
   2276       /*
   2277        * Group the denominations by {cipher, value, fees, age_mask}.
   2278        *
   2279        * For each group we save the group meta-data and the list of
   2280        * denominations in this group as a json-blob in the multihashmap
   2281        * denominations_by_group.
   2282        */
   2283       {
   2284         struct GroupData *group;
   2285         json_t *entry;
   2286         struct GNUNET_HashCode key;
   2287         struct TALER_DenominationGroup meta = {
   2288           .cipher = dk->denom_pub.bsign_pub_key->cipher,
   2289           .value = dk->meta.value,
   2290           .fees = dk->meta.fees,
   2291           .age_mask = dk->meta.age_mask,
   2292         };
   2293 
   2294         /* Search the group/JSON-blob for the key */
   2295         TALER_denomination_group_get_key (&meta,
   2296                                           &key);
   2297         group = GNUNET_CONTAINER_multihashmap_get (
   2298           denominations_by_group,
   2299           &key);
   2300         if (NULL == group)
   2301         {
   2302           /* There is no group for this meta-data yet, so we create a new group */
   2303           bool age_restricted = meta.age_mask.bits != 0;
   2304           const char *cipher;
   2305 
   2306           group = GNUNET_new (struct GroupData);
   2307           switch (meta.cipher)
   2308           {
   2309           case GNUNET_CRYPTO_BSA_RSA:
   2310             cipher = age_restricted ? "RSA+age_restricted" : "RSA";
   2311             break;
   2312           case GNUNET_CRYPTO_BSA_CS:
   2313             cipher = age_restricted ? "CS+age_restricted" : "CS";
   2314             break;
   2315           default:
   2316             GNUNET_assert (false);
   2317           }
   2318           /* Create a new array for the denominations in this group */
   2319           group->list = json_array ();
   2320           GNUNET_assert (NULL != group->list);
   2321           group->json = GNUNET_JSON_PACK (
   2322             GNUNET_JSON_pack_string ("cipher",
   2323                                      cipher),
   2324             GNUNET_JSON_pack_array_steal ("denoms",
   2325                                           group->list),
   2326             TALER_JSON_PACK_DENOM_FEES ("fee",
   2327                                         &meta.fees),
   2328             TALER_JSON_pack_amount ("value",
   2329                                     &meta.value));
   2330           GNUNET_assert (NULL != group->json);
   2331           if (age_restricted)
   2332           {
   2333             GNUNET_assert (
   2334               0 ==
   2335               json_object_set_new (group->json,
   2336                                    "age_mask",
   2337                                    json_integer (
   2338                                      meta.age_mask.bits)));
   2339             /* Remember that we have found at least _one_ age restricted denomination */
   2340             has_age_restricted_denomination = true;
   2341           }
   2342           group->group_off
   2343             = json_array_size (grouped_denominations);
   2344           GNUNET_assert (0 ==
   2345                          json_array_append_new (
   2346                            grouped_denominations,
   2347                            group->json));
   2348           GNUNET_assert (
   2349             GNUNET_OK ==
   2350             GNUNET_CONTAINER_multihashmap_put (denominations_by_group,
   2351                                                &key,
   2352                                                group,
   2353                                                GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
   2354         }
   2355 
   2356         /* Now that we have found/created the right group, add the
   2357            denomination to the list */
   2358         {
   2359           struct GNUNET_JSON_PackSpec key_spec;
   2360           bool private_key_lost;
   2361 
   2362           private_key_lost
   2363             = TEH_SECMOD_denom_priv_check_lost (&dk->h_denom_pub);
   2364           switch (meta.cipher)
   2365           {
   2366           case GNUNET_CRYPTO_BSA_RSA:
   2367             key_spec =
   2368               GNUNET_JSON_pack_rsa_public_key (
   2369                 "rsa_pub",
   2370                 dk->denom_pub.bsign_pub_key->details.rsa_public_key);
   2371             break;
   2372           case GNUNET_CRYPTO_BSA_CS:
   2373             key_spec =
   2374               GNUNET_JSON_pack_data_varsize (
   2375                 "cs_pub",
   2376                 &dk->denom_pub.bsign_pub_key->details.cs_public_key,
   2377                 sizeof (dk->denom_pub.bsign_pub_key->details.cs_public_key));
   2378             break;
   2379           default:
   2380             GNUNET_assert (false);
   2381           }
   2382 
   2383           entry = GNUNET_JSON_PACK (
   2384             GNUNET_JSON_pack_data_auto ("master_sig",
   2385                                         &dk->master_sig),
   2386             GNUNET_JSON_pack_allow_null (
   2387               private_key_lost
   2388               ? GNUNET_JSON_pack_bool ("lost",
   2389                                        true)
   2390               : GNUNET_JSON_pack_string ("dummy",
   2391                                          NULL)),
   2392             GNUNET_JSON_pack_timestamp ("stamp_start",
   2393                                         dk->meta.start),
   2394             GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw",
   2395                                         dk->meta.expire_withdraw),
   2396             GNUNET_JSON_pack_timestamp ("stamp_expire_deposit",
   2397                                         dk->meta.expire_deposit),
   2398             GNUNET_JSON_pack_timestamp ("stamp_expire_legal",
   2399                                         dk->meta.expire_legal),
   2400             key_spec
   2401             );
   2402           GNUNET_assert (NULL != entry);
   2403         }
   2404 
   2405         /* Build up the running hash of all master signatures of the
   2406            denominations */
   2407         append_signature (&sig_ctx,
   2408                           group->group_off,
   2409                           (unsigned int) json_array_size (group->list),
   2410                           &dk->master_sig);
   2411         /* Finally, add the denomination to the list of denominations in this
   2412            group */
   2413         GNUNET_assert (json_is_array (group->list));
   2414         GNUNET_assert (0 ==
   2415                        json_array_append_new (group->list,
   2416                                               entry));
   2417       }
   2418     } /* loop over heap ends */
   2419 
   2420     GNUNET_CONTAINER_multihashmap_iterate (denominations_by_group,
   2421                                            &free_group,
   2422                                            NULL);
   2423     GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
   2424   }
   2425   GNUNET_CONTAINER_heap_destroy (heap);
   2426 
   2427   if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time))
   2428   {
   2429     struct GNUNET_HashCode hc;
   2430 
   2431     compute_msig_hash (&sig_ctx,
   2432                        &hc);
   2433     if (GNUNET_OK !=
   2434         create_krd (ksh,
   2435                     &hc,
   2436                     last_cherry_pick_date,
   2437                     sctx.signkeys,
   2438                     recoup,
   2439                     grouped_denominations))
   2440     {
   2441       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2442                   "Failed to generate key response data for %s\n",
   2443                   GNUNET_TIME_timestamp2s (last_cherry_pick_date));
   2444       goto CLEANUP;
   2445     }
   2446     ksh->management_only = false;
   2447 
   2448     /* Sanity check:  Make sure that age restriction is enabled IFF at least
   2449      * one age restricted denomination exist */
   2450     if (! has_age_restricted_denomination && TEH_age_restriction_enabled)
   2451     {
   2452       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2453                   "Age restriction is enabled, but NO denominations with age restriction found!\n");
   2454       goto CLEANUP;
   2455     }
   2456     else if (has_age_restricted_denomination && ! TEH_age_restriction_enabled)
   2457     {
   2458       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2459                   "Age restriction is NOT enabled, but denominations with age restriction found!\n")
   2460       ;
   2461       goto CLEANUP;
   2462     }
   2463   }
   2464   else
   2465   {
   2466     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2467                 "No denomination keys available. Refusing to generate /keys response.\n");
   2468   }
   2469   ret = GNUNET_OK;
   2470 
   2471 CLEANUP:
   2472   GNUNET_array_grow (sig_ctx.elements,
   2473                      sig_ctx.elements_size,
   2474                      0);
   2475   json_decref (grouped_denominations);
   2476   if (NULL != sctx.signkeys)
   2477     json_decref (sctx.signkeys);
   2478   json_decref (recoup);
   2479   return ret;
   2480 }
   2481 
   2482 
   2483 /**
   2484  * Called with information about global fees.
   2485  *
   2486  * @param ksh `struct TEH_KeyStateHandle *` we are building
   2487  * @param fees the global fees we charge
   2488  * @param purse_timeout when do purses time out
   2489  * @param history_expiration how long are account histories preserved
   2490  * @param purse_account_limit how many purses are free per account
   2491  * @param start_date from when are these fees valid (start date)
   2492  * @param end_date until when are these fees valid (end date, exclusive)
   2493  * @param master_sig master key signature affirming that this is the correct
   2494  *                   fee (of purpose #TALER_SIGNATURE_MASTER_GLOBAL_FEES)
   2495  */
   2496 static void
   2497 global_fee_info_cb (
   2498   struct TEH_KeyStateHandle *ksh,
   2499   const struct TALER_GlobalFeeSet *fees,
   2500   struct GNUNET_TIME_Relative purse_timeout,
   2501   struct GNUNET_TIME_Relative history_expiration,
   2502   uint32_t purse_account_limit,
   2503   struct GNUNET_TIME_Timestamp start_date,
   2504   struct GNUNET_TIME_Timestamp end_date,
   2505   const struct TALER_MasterSignatureP *master_sig)
   2506 {
   2507   struct TEH_GlobalFee *gf;
   2508 
   2509   if (GNUNET_OK !=
   2510       TALER_exchange_offline_global_fee_verify (
   2511         start_date,
   2512         end_date,
   2513         fees,
   2514         purse_timeout,
   2515         history_expiration,
   2516         purse_account_limit,
   2517         &TEH_master_public_key,
   2518         master_sig))
   2519   {
   2520     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2521                 "Database has global fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
   2522     return;
   2523   }
   2524   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2525               "Found global fees with %u purses\n",
   2526               purse_account_limit);
   2527   gf = GNUNET_new (struct TEH_GlobalFee);
   2528   gf->start_date = start_date;
   2529   gf->end_date = end_date;
   2530   gf->fees = *fees;
   2531   gf->purse_timeout = purse_timeout;
   2532   gf->history_expiration = history_expiration;
   2533   gf->purse_account_limit = purse_account_limit;
   2534   gf->master_sig = *master_sig;
   2535   GNUNET_CONTAINER_DLL_insert (ksh->gf_head,
   2536                                ksh->gf_tail,
   2537                                gf);
   2538   GNUNET_assert (
   2539     0 ==
   2540     json_array_append_new (
   2541       ksh->global_fees,
   2542       GNUNET_JSON_PACK (
   2543         GNUNET_JSON_pack_timestamp ("start_date",
   2544                                     start_date),
   2545         GNUNET_JSON_pack_timestamp ("end_date",
   2546                                     end_date),
   2547         TALER_JSON_PACK_GLOBAL_FEES (fees),
   2548         GNUNET_JSON_pack_time_rel ("history_expiration",
   2549                                    history_expiration),
   2550         GNUNET_JSON_pack_time_rel ("purse_timeout",
   2551                                    purse_timeout),
   2552         GNUNET_JSON_pack_uint64 ("purse_account_limit",
   2553                                  purse_account_limit),
   2554         GNUNET_JSON_pack_data_auto ("master_sig",
   2555                                     master_sig))));
   2556 }
   2557 
   2558 
   2559 /**
   2560  * Create a key state.
   2561  *
   2562  * @param management_only if we should NOT run 'finish_keys_response()'
   2563  *                  because we only need the state for the /management/keys API
   2564  * @return NULL on error (i.e. failed to access database)
   2565  */
   2566 static struct TEH_KeyStateHandle *
   2567 build_key_state (bool management_only)
   2568 {
   2569   struct TEH_KeyStateHandle *ksh;
   2570   enum GNUNET_DB_QueryStatus qs;
   2571 
   2572   ksh = GNUNET_new (struct TEH_KeyStateHandle);
   2573   ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS;
   2574   ksh->reload_time = GNUNET_TIME_timestamp_get ();
   2575   /* We must use the key_generation from when we STARTED the process! */
   2576   ksh->key_generation = key_generation;
   2577   ksh->denomserial_map = GNUNET_CONTAINER_multihashmap32_create (1024);
   2578   ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024,
   2579                                                             true);
   2580   ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32,
   2581                                                            false /* MUST be false! */
   2582                                                            );
   2583   ksh->auditors = json_array ();
   2584   GNUNET_assert (NULL != ksh->auditors);
   2585   /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */
   2586   GNUNET_break (GNUNET_OK ==
   2587                 TALER_EXCHANGEDB_preflight (TEH_pg));
   2588   if (NULL != ksh->global_fees)
   2589     json_decref (ksh->global_fees);
   2590   ksh->global_fees = json_array ();
   2591   qs = TALER_EXCHANGEDB_iterate_global_fees (TEH_pg,
   2592                                              &global_fee_info_cb,
   2593                                              ksh);
   2594   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2595               "Loading global fees from DB: %d\n",
   2596               qs);
   2597   if (qs < 0)
   2598   {
   2599     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   2600     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
   2601     destroy_key_state (ksh);
   2602     return NULL;
   2603   }
   2604   qs = TALER_EXCHANGEDB_iterate_denominations (TEH_pg,
   2605                                                &denomination_info_cb,
   2606                                                ksh);
   2607   if (qs < 0)
   2608   {
   2609     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   2610     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
   2611     destroy_key_state (ksh);
   2612     return NULL;
   2613   }
   2614   /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */
   2615   qs = TALER_EXCHANGEDB_iterate_active_signkeys (TEH_pg,
   2616                                                  &signkey_info_cb,
   2617                                                  ksh);
   2618   if (qs < 0)
   2619   {
   2620     GNUNET_break (0);
   2621     destroy_key_state (ksh);
   2622     return NULL;
   2623   }
   2624   qs = TALER_EXCHANGEDB_iterate_auditor_denominations (TEH_pg,
   2625                                                        &auditor_denom_cb,
   2626                                                        ksh);
   2627   if (qs < 0)
   2628   {
   2629     GNUNET_break (0);
   2630     destroy_key_state (ksh);
   2631     return NULL;
   2632   }
   2633   qs = TALER_EXCHANGEDB_iterate_active_auditors (TEH_pg,
   2634                                                  &auditor_info_cb,
   2635                                                  ksh);
   2636   if (qs < 0)
   2637   {
   2638     GNUNET_break (0);
   2639     destroy_key_state (ksh);
   2640     return NULL;
   2641   }
   2642 
   2643   if (management_only)
   2644   {
   2645     ksh->management_only = true;
   2646     return ksh;
   2647   }
   2648 
   2649   if (GNUNET_OK !=
   2650       finish_keys_response (ksh))
   2651   {
   2652     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2653                 "Could not finish /keys response (required data not configured yet)\n");
   2654     destroy_key_state (ksh);
   2655     return NULL;
   2656   }
   2657   TEH_resume_keys_requests (false);
   2658   return ksh;
   2659 }
   2660 
   2661 
   2662 void
   2663 TEH_keys_update_states ()
   2664 {
   2665   struct GNUNET_DB_EventHeaderP es = {
   2666     .size = htons (sizeof (es)),
   2667     .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED),
   2668   };
   2669 
   2670   TALER_EXCHANGEDB_event_notify (TEH_pg,
   2671                                  &es,
   2672                                  NULL,
   2673                                  0);
   2674   key_generation++;
   2675   TEH_resume_keys_requests (false);
   2676 }
   2677 
   2678 
   2679 static struct TEH_KeyStateHandle *
   2680 keys_get_state (bool management_only)
   2681 {
   2682   struct TEH_KeyStateHandle *old_ksh;
   2683   struct TEH_KeyStateHandle *ksh;
   2684 
   2685   old_ksh = key_state;
   2686   if (NULL == old_ksh)
   2687   {
   2688     ksh = build_key_state (management_only);
   2689     if (NULL == ksh)
   2690       return NULL;
   2691     key_state = ksh;
   2692     return ksh;
   2693   }
   2694   if ( (old_ksh->key_generation < key_generation) ||
   2695        (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) )
   2696   {
   2697     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2698                 "Rebuilding /keys, generation upgrade from %llu to %llu\n",
   2699                 (unsigned long long) old_ksh->key_generation,
   2700                 (unsigned long long) key_generation);
   2701     ksh = build_key_state (management_only);
   2702     key_state = ksh;
   2703     destroy_key_state (old_ksh);
   2704     return ksh;
   2705   }
   2706   TEH_SECMOD_sync_key_helpers ();
   2707   return old_ksh;
   2708 }
   2709 
   2710 
   2711 struct TEH_KeyStateHandle *
   2712 TEH_keys_get_state_for_management_only (void)
   2713 {
   2714   return keys_get_state (true);
   2715 }
   2716 
   2717 
   2718 struct TEH_KeyStateHandle *
   2719 TEH_keys_get_state (void)
   2720 {
   2721   struct TEH_KeyStateHandle *ksh;
   2722 
   2723   ksh = keys_get_state (false);
   2724   if (NULL == ksh)
   2725     return NULL;
   2726 
   2727   if (ksh->management_only)
   2728   {
   2729     if (GNUNET_OK !=
   2730         finish_keys_response (ksh))
   2731       return NULL;
   2732   }
   2733 
   2734   return ksh;
   2735 }
   2736 
   2737 
   2738 const struct TEH_GlobalFee *
   2739 TEH_keys_global_fee_by_time (
   2740   struct TEH_KeyStateHandle *ksh,
   2741   struct GNUNET_TIME_Timestamp ts)
   2742 {
   2743   for (const struct TEH_GlobalFee *gf = ksh->gf_head;
   2744        NULL != gf;
   2745        gf = gf->next)
   2746   {
   2747     if (GNUNET_TIME_timestamp_cmp (ts,
   2748                                    >=,
   2749                                    gf->start_date) &&
   2750         GNUNET_TIME_timestamp_cmp (ts,
   2751                                    <,
   2752                                    gf->end_date))
   2753       return gf;
   2754   }
   2755   return NULL;
   2756 }
   2757 
   2758 
   2759 struct TEH_DenominationKey *
   2760 TEH_keys_denomination_by_hash (
   2761   const struct TALER_DenominationHashP *h_denom_pub,
   2762   struct MHD_Connection *conn,
   2763   enum MHD_Result *mret)
   2764 {
   2765   struct TEH_KeyStateHandle *ksh;
   2766 
   2767   ksh = TEH_keys_get_state ();
   2768   if (NULL == ksh)
   2769   {
   2770     *mret = TALER_MHD_reply_with_error (conn,
   2771                                         MHD_HTTP_SERVICE_UNAVAILABLE,
   2772                                         TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
   2773                                         NULL);
   2774     return NULL;
   2775   }
   2776 
   2777   return TEH_keys_denomination_by_hash_from_state (ksh,
   2778                                                    h_denom_pub,
   2779                                                    conn,
   2780                                                    mret);
   2781 }
   2782 
   2783 
   2784 struct TEH_DenominationKey *
   2785 TEH_keys_denomination_by_hash_from_state (
   2786   const struct TEH_KeyStateHandle *ksh,
   2787   const struct TALER_DenominationHashP *h_denom_pub,
   2788   struct MHD_Connection *conn,
   2789   enum MHD_Result *mret)
   2790 {
   2791   struct TEH_DenominationKey *dk;
   2792 
   2793   dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
   2794                                           &h_denom_pub->hash);
   2795   if (NULL == dk)
   2796   {
   2797     if (NULL == conn)
   2798       return NULL;
   2799     *mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
   2800                                                        h_denom_pub);
   2801     return NULL;
   2802   }
   2803   return dk;
   2804 }
   2805 
   2806 
   2807 struct TEH_DenominationKey *
   2808 TEH_keys_denomination_by_serial_from_state (
   2809   const struct TEH_KeyStateHandle *ksh,
   2810   uint64_t denom_serial)
   2811 {
   2812   struct TEH_DenominationKey *dk;
   2813   uint32_t serial32 = (uint32_t) denom_serial;
   2814 
   2815   GNUNET_assert (denom_serial == (uint64_t) serial32);
   2816   dk = GNUNET_CONTAINER_multihashmap32_get (ksh->denomserial_map,
   2817                                             serial32);
   2818   return dk;
   2819 }
   2820 
   2821 
   2822 enum TALER_ErrorCode
   2823 TEH_keys_exchange_sign_ (
   2824   const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
   2825   struct TALER_ExchangePublicKeyP *pub,
   2826   struct TALER_ExchangeSignatureP *sig)
   2827 {
   2828   struct TEH_KeyStateHandle *ksh;
   2829 
   2830   ksh = TEH_keys_get_state ();
   2831   if (NULL == ksh)
   2832   {
   2833     /* This *can* happen if the exchange's crypto helper is not running
   2834        or had some bad error. */
   2835     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2836                 "Cannot sign request, no valid signing keys available.\n");
   2837     return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
   2838   }
   2839   return TEH_keys_exchange_sign2_ (ksh,
   2840                                    purpose,
   2841                                    pub,
   2842                                    sig);
   2843 }
   2844 
   2845 
   2846 enum TALER_ErrorCode
   2847 TEH_keys_exchange_sign2_ (
   2848   void *cls,
   2849   const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
   2850   struct TALER_ExchangePublicKeyP *pub,
   2851   struct TALER_ExchangeSignatureP *sig)
   2852 {
   2853   struct TEH_KeyStateHandle *ksh = cls;
   2854   enum TALER_ErrorCode ec;
   2855 
   2856   TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_EDDSA]++;
   2857   ec = TEH_SECMOD_exchange_sign (purpose,
   2858                                  pub,
   2859                                  sig);
   2860   if (TALER_EC_NONE != ec)
   2861     return ec;
   2862   {
   2863     /* Here we check here that 'pub' is set to an exchange public key that is
   2864        actually signed by the master key! Otherwise, we happily continue to
   2865        use key material even if the offline signatures have not been made
   2866        yet! */
   2867     struct GNUNET_PeerIdentity pid;
   2868     struct SigningKey *sk;
   2869 
   2870     pid.public_key = pub->eddsa_pub;
   2871     sk = GNUNET_CONTAINER_multipeermap_get (ksh->signkey_map,
   2872                                             &pid);
   2873     if (NULL == sk)
   2874     {
   2875       /* just to be safe, zero out the (valid) signature, as the key
   2876          should not or no longer be used */
   2877       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2878                   "Cannot sign, offline key signatures are missing!\n");
   2879       memset (sig,
   2880               0,
   2881               sizeof (*sig));
   2882       return TALER_EC_EXCHANGE_SIGNKEY_HELPER_OFFLINE_MISSING;
   2883     }
   2884   }
   2885   return ec;
   2886 }
   2887 
   2888 
   2889 /**
   2890  * Comparator used for a binary search by cherry_pick_date for @a key in the
   2891  * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions.
   2892  *
   2893  * @param key pointer to a `struct GNUNET_TIME_Timestamp`
   2894  * @param value pointer to a `struct KeysResponseData` array entry
   2895  * @return 0 if time matches, -1 if key is smaller, 1 if key is larger
   2896  */
   2897 static int
   2898 krd_search_comparator (const void *key,
   2899                        const void *value)
   2900 {
   2901   const struct GNUNET_TIME_Timestamp *kd = key;
   2902   const struct KeysResponseData *krd = value;
   2903 
   2904   if (GNUNET_TIME_timestamp_cmp (*kd,
   2905                                  >,
   2906                                  krd->cherry_pick_date))
   2907     return -1;
   2908   if (GNUNET_TIME_timestamp_cmp (*kd,
   2909                                  <,
   2910                                  krd->cherry_pick_date))
   2911     return 1;
   2912   return 0;
   2913 }
   2914 
   2915 
   2916 enum MHD_Result
   2917 TEH_keys_get_handler (struct TEH_RequestContext *rc,
   2918                       const char *const args[])
   2919 {
   2920   struct GNUNET_TIME_Timestamp last_issue_date;
   2921   const char *etag;
   2922 
   2923   etag = MHD_lookup_connection_value (rc->connection,
   2924                                       MHD_HEADER_KIND,
   2925                                       MHD_HTTP_HEADER_IF_NONE_MATCH);
   2926   (void) args;
   2927   {
   2928     const char *have_cherrypick;
   2929 
   2930     have_cherrypick = MHD_lookup_connection_value (rc->connection,
   2931                                                    MHD_GET_ARGUMENT_KIND,
   2932                                                    "last_issue_date");
   2933     if (NULL != have_cherrypick)
   2934     {
   2935       unsigned long long cherrypickn;
   2936 
   2937       if (1 !=
   2938           sscanf (have_cherrypick,
   2939                   "%llu",
   2940                   &cherrypickn))
   2941       {
   2942         GNUNET_break_op (0);
   2943         return TALER_MHD_reply_with_error (rc->connection,
   2944                                            MHD_HTTP_BAD_REQUEST,
   2945                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
   2946                                            have_cherrypick);
   2947       }
   2948       /* The following multiplication may overflow; but this should not really
   2949          be a problem, as giving back 'older' data than what the client asks for
   2950          (given that the client asks for data in the distant future) is not
   2951          problematic */
   2952       last_issue_date = GNUNET_TIME_timestamp_from_s (cherrypickn);
   2953     }
   2954     else
   2955     {
   2956       last_issue_date = GNUNET_TIME_UNIT_ZERO_TS;
   2957     }
   2958   }
   2959 
   2960   {
   2961     struct TEH_KeyStateHandle *ksh;
   2962     const struct KeysResponseData *krd;
   2963 
   2964     ksh = TEH_keys_get_state ();
   2965     if ( (NULL == ksh) ||
   2966          (0 == ksh->krd_array_length) )
   2967     {
   2968       if ( ( (SKR_LIMIT <= skr_size) &&
   2969              (GNUNET_TIME_relative_cmp (
   2970                 GNUNET_TIME_absolute_get_duration (rc->start_time),
   2971                 >,
   2972                 GNUNET_TIME_UNIT_SECONDS)) ) ||
   2973            TEH_suicide)
   2974       {
   2975         return TALER_MHD_reply_with_error (
   2976           rc->connection,
   2977           MHD_HTTP_SERVICE_UNAVAILABLE,
   2978           TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
   2979           TEH_suicide
   2980           ? "server terminating"
   2981           : "too many connections suspended waiting on /keys");
   2982       }
   2983       return suspend_request (rc->connection);
   2984     }
   2985     krd = bsearch (&last_issue_date,
   2986                    ksh->krd_array,
   2987                    ksh->krd_array_length,
   2988                    sizeof (struct KeysResponseData),
   2989                    &krd_search_comparator);
   2990     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2991                 "Filtering /keys by cherry pick date %s found entry %u/%u\n",
   2992                 GNUNET_TIME_timestamp2s (last_issue_date),
   2993                 (unsigned int) (krd - ksh->krd_array),
   2994                 ksh->krd_array_length);
   2995     if ( (NULL == krd) &&
   2996          (ksh->krd_array_length > 0) )
   2997     {
   2998       if (! GNUNET_TIME_absolute_is_zero (last_issue_date.abs_time))
   2999         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3000                     "Client provided invalid cherry picking timestamp %s, returning full response\n",
   3001                     GNUNET_TIME_timestamp2s (last_issue_date));
   3002       krd = &ksh->krd_array[ksh->krd_array_length - 1];
   3003     }
   3004     if (NULL == krd)
   3005     {
   3006       /* Likely keys not ready *yet*.
   3007          Wait until they are. */
   3008       return suspend_request (rc->connection);
   3009     }
   3010     if ( (NULL != etag) &&
   3011          (0 == strcmp (etag,
   3012                        krd->etag)) )
   3013       return TEH_RESPONSE_reply_not_modified (rc->connection,
   3014                                               krd->etag,
   3015                                               &setup_general_response_headers,
   3016                                               ksh);
   3017 
   3018     return MHD_queue_response (
   3019       rc->connection,
   3020       MHD_HTTP_OK,
   3021       (TALER_MHD_CT_DEFLATE ==
   3022        TALER_MHD_can_compress (rc->connection,
   3023                                TALER_MHD_CT_DEFLATE))
   3024       ? krd->response_compressed
   3025       : krd->response_uncompressed);
   3026   }
   3027 }
   3028 
   3029 
   3030 /**
   3031  * Closure for #add_future_denomkey_cb and #add_future_signkey_cb.
   3032  */
   3033 struct FutureBuilderContext
   3034 {
   3035   /**
   3036    * Our key state.
   3037    */
   3038   struct TEH_KeyStateHandle *ksh;
   3039 
   3040   /**
   3041    * Array of denomination keys.
   3042    */
   3043   json_t *denoms;
   3044 
   3045   /**
   3046    * Array of signing keys.
   3047    */
   3048   json_t *signkeys;
   3049 
   3050 };
   3051 
   3052 
   3053 /**
   3054  * Function called on all of our current and future denomination keys
   3055  * known to the helper process. Filters out those that are current
   3056  * and adds the remaining denomination keys (with their configuration
   3057  * data) to the JSON array.
   3058  *
   3059  * @param cls the `struct FutureBuilderContext *`
   3060  * @param h_denom_pub hash of the denomination public key
   3061  * @param value a `struct HelperDenomination`
   3062  * @return #GNUNET_OK (continue to iterate)
   3063  */
   3064 static enum GNUNET_GenericReturnValue
   3065 add_future_denomkey_cb (void *cls,
   3066                         const struct GNUNET_HashCode *h_denom_pub,
   3067                         void *value)
   3068 {
   3069   struct FutureBuilderContext *fbc = cls;
   3070   struct HelperDenomination *hd = value;
   3071   struct TEH_DenominationKey *dk;
   3072   struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0};
   3073 
   3074   dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map,
   3075                                           h_denom_pub);
   3076   if (NULL != dk)
   3077     return GNUNET_OK; /* skip: this key is already active! */
   3078   if (GNUNET_TIME_relative_is_zero (hd->validity_duration))
   3079     return GNUNET_OK; /* this key already expired! */
   3080   meta.start = hd->start_time;
   3081   meta.expire_withdraw = GNUNET_TIME_absolute_to_timestamp (
   3082     GNUNET_TIME_absolute_add (meta.start.abs_time,
   3083                               hd->validity_duration));
   3084   if (GNUNET_OK !=
   3085       TEH_CONFIG_load_denom_data (hd->section_name,
   3086                                   &meta))
   3087   {
   3088     /* Woops, couldn't determine fee structure!? */
   3089     return GNUNET_OK;
   3090   }
   3091   GNUNET_assert (
   3092     0 ==
   3093     json_array_append_new (
   3094       fbc->denoms,
   3095       GNUNET_JSON_PACK (
   3096         TALER_JSON_pack_amount ("value",
   3097                                 &meta.value),
   3098         GNUNET_JSON_pack_timestamp ("stamp_start",
   3099                                     meta.start),
   3100         GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw",
   3101                                     meta.expire_withdraw),
   3102         GNUNET_JSON_pack_timestamp ("stamp_expire_deposit",
   3103                                     meta.expire_deposit),
   3104         GNUNET_JSON_pack_timestamp ("stamp_expire_legal",
   3105                                     meta.expire_legal),
   3106         TALER_JSON_pack_denom_pub ("denom_pub",
   3107                                    &hd->denom_pub),
   3108         TALER_JSON_PACK_DENOM_FEES ("fee",
   3109                                     &meta.fees),
   3110         GNUNET_JSON_pack_data_auto ("denom_secmod_sig",
   3111                                     &hd->sm_sig),
   3112         GNUNET_JSON_pack_string ("section_name",
   3113                                  hd->section_name))));
   3114   return GNUNET_OK;
   3115 }
   3116 
   3117 
   3118 /**
   3119  * Function called on all of our current and future exchange signing keys
   3120  * known to the helper process. Filters out those that are current
   3121  * and adds the remaining signing keys (with their configuration
   3122  * data) to the JSON array.
   3123  *
   3124  * @param cls the `struct FutureBuilderContext *`
   3125  * @param pid actually the exchange public key (type disguised)
   3126  * @param value a `struct HelperDenomination`
   3127  * @return #GNUNET_OK (continue to iterate)
   3128  */
   3129 static enum GNUNET_GenericReturnValue
   3130 add_future_signkey_cb (void *cls,
   3131                        const struct GNUNET_PeerIdentity *pid,
   3132                        void *value)
   3133 {
   3134   struct FutureBuilderContext *fbc = cls;
   3135   struct HelperSignkey *hsk = value;
   3136   struct SigningKey *sk;
   3137   struct GNUNET_TIME_Timestamp stamp_expire;
   3138   struct GNUNET_TIME_Timestamp legal_end;
   3139 
   3140   sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map,
   3141                                           pid);
   3142   if (NULL != sk)
   3143     return GNUNET_OK; /* skip: this key is already active */
   3144   if (GNUNET_TIME_relative_is_zero (hsk->validity_duration))
   3145     return GNUNET_OK; /* this key already expired! */
   3146   stamp_expire = GNUNET_TIME_absolute_to_timestamp (
   3147     GNUNET_TIME_absolute_add (hsk->start_time.abs_time,
   3148                               hsk->validity_duration));
   3149   legal_end = GNUNET_TIME_absolute_to_timestamp (
   3150     GNUNET_TIME_absolute_add (stamp_expire.abs_time,
   3151                               signkey_legal_duration));
   3152   GNUNET_assert (0 ==
   3153                  json_array_append_new (
   3154                    fbc->signkeys,
   3155                    GNUNET_JSON_PACK (
   3156                      GNUNET_JSON_pack_data_auto ("key",
   3157                                                  &hsk->exchange_pub),
   3158                      GNUNET_JSON_pack_timestamp ("stamp_start",
   3159                                                  hsk->start_time),
   3160                      GNUNET_JSON_pack_timestamp ("stamp_expire",
   3161                                                  stamp_expire),
   3162                      GNUNET_JSON_pack_timestamp ("stamp_end",
   3163                                                  legal_end),
   3164                      GNUNET_JSON_pack_data_auto ("signkey_secmod_sig",
   3165                                                  &hsk->sm_sig))));
   3166   return GNUNET_OK;
   3167 }
   3168 
   3169 
   3170 enum MHD_Result
   3171 TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
   3172                                       struct MHD_Connection *connection)
   3173 {
   3174   struct TEH_KeyStateHandle *ksh;
   3175   json_t *reply;
   3176 
   3177   (void) rh;
   3178   ksh = TEH_keys_get_state_for_management_only ();
   3179   if (NULL == ksh)
   3180   {
   3181     return TALER_MHD_reply_with_error (connection,
   3182                                        MHD_HTTP_SERVICE_UNAVAILABLE,
   3183                                        TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
   3184                                        "no key state");
   3185   }
   3186   TEH_SECMOD_sync_key_helpers ();
   3187   if (NULL == ksh->management_keys_reply)
   3188   {
   3189     struct FutureBuilderContext fbc = {
   3190       .ksh = ksh,
   3191     };
   3192 
   3193     if (! TEH_SECMOD_have_denom_sm_pub ())
   3194     {
   3195       /* Either IPC failed, or neither helper had any denominations configured. */
   3196       return TALER_MHD_reply_with_error (connection,
   3197                                          MHD_HTTP_BAD_GATEWAY,
   3198                                          TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE,
   3199                                          NULL);
   3200     }
   3201     if (! TEH_SECMOD_have_esign_sm_pub ())
   3202     {
   3203       return TALER_MHD_reply_with_error (connection,
   3204                                          MHD_HTTP_BAD_GATEWAY,
   3205                                          TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE,
   3206                                          NULL);
   3207     }
   3208     fbc.denoms = json_array ();
   3209     GNUNET_assert (NULL != fbc.denoms);
   3210     fbc.signkeys = json_array ();
   3211     GNUNET_assert (NULL != fbc.signkeys);
   3212     TEH_SECMOD_iterate_denom_keys (&add_future_denomkey_cb,
   3213                                    &fbc);
   3214     TEH_SECMOD_iterate_esign_keys (&add_future_signkey_cb,
   3215                                    &fbc);
   3216     reply = GNUNET_JSON_PACK (
   3217       GNUNET_JSON_pack_array_steal ("future_denoms",
   3218                                     fbc.denoms),
   3219       GNUNET_JSON_pack_array_steal ("future_signkeys",
   3220                                     fbc.signkeys),
   3221       GNUNET_JSON_pack_data_auto ("master_pub",
   3222                                   &TEH_master_public_key));
   3223     {
   3224       json_t *sj = TEH_SECMOD_get_sm_pubs_as_json ();
   3225 
   3226       json_object_update (reply,
   3227                           sj);
   3228       json_decref (sj);
   3229     }
   3230     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3231                 "Returning GET /management/keys response:\n");
   3232     if (NULL == reply)
   3233     {
   3234       GNUNET_break (0);
   3235       return TALER_MHD_reply_with_error (connection,
   3236                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   3237                                          TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
   3238                                          NULL);
   3239     }
   3240     GNUNET_assert (NULL == ksh->management_keys_reply);
   3241     ksh->management_keys_reply = reply;
   3242   }
   3243   else
   3244   {
   3245     reply = ksh->management_keys_reply;
   3246   }
   3247   return TALER_MHD_reply_json (connection,
   3248                                reply,
   3249                                MHD_HTTP_OK);
   3250 }
   3251 
   3252 
   3253 /* end of taler-exchange-httpd_keys.c */