donau

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

donau_api_handle.c (26911B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it
      6   under the terms of the GNU General Public License as published
      7   by the Free Software Foundation; either version 3, or (at your
      8   option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file lib/donau_api_handle.c
     22  * @brief Implementation of the "handle" component of the donau's HTTP API
     23  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
     24  * @author Christian Grothoff
     25  * @author Lukas Matyja
     26  */
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include <taler/taler_json_lib.h>
     29 #include "donau_service.h"
     30 #include "donau_api_curl_defaults.h"
     31 #include "donau_util.h"
     32 #include "donau_json_lib.h"
     33 #include <sodium.h>
     34 
     35 
     36 /**
     37  * Which version of the Donau protocol is implemented
     38  * by this library?  Used to determine compatibility.
     39  */
     40 #define DONAU_PROTOCOL_CURRENT 0
     41 
     42 /**
     43  * How many versions are we backwards compatible with?
     44  */
     45 #define DONAU_PROTOCOL_AGE 0
     46 
     47 /**
     48  * Set to 1 for extra debug logging.
     49  */
     50 #define DEBUG 0
     51 
     52 /**
     53  * Current version for (local) JSON serialization of persisted
     54  * /keys data.
     55  */
     56 #define DONAU_SERIALIZATION_FORMAT_VERSION 0
     57 
     58 /**
     59  * How far off do we allow key liftimes to be?
     60  */
     61 #define LIFETIME_TOLERANCE GNUNET_TIME_UNIT_HOURS
     62 
     63 /**
     64  * If the "Expire" cache control header is missing, for
     65  * how long do we assume the reply to be valid at least?
     66  */
     67 #define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS
     68 
     69 /**
     70  * If the "Expire" cache control header is missing, for
     71  * how long do we assume the reply to be valid at least?
     72  */
     73 #define MINIMUM_EXPIRATION GNUNET_TIME_relative_multiply ( \
     74           GNUNET_TIME_UNIT_MINUTES, 2)
     75 
     76 
     77 /**
     78  * Handle for a GET /keys request.
     79  */
     80 struct DONAU_GetKeysHandle
     81 {
     82 
     83   /**
     84    * The donau base URL (i.e. "http://donau.taler.net/")
     85    */
     86   char *donau_url;
     87 
     88   /**
     89    * The url for the /keys request.
     90    */
     91   char *url;
     92 
     93   /**
     94    * Previous /keys response, NULL for none.
     95    */
     96   struct DONAU_Keys *prev_keys; // not used, as keys are always completely reloaded
     97 
     98   /**
     99    * Entry for this request with the `struct GNUNET_CURL_Context`.
    100    */
    101   struct GNUNET_CURL_Job *job;
    102 
    103   /**
    104    * Expiration time according to "Expire:" header.
    105    * 0 if not provided by the server.
    106    */
    107   struct GNUNET_TIME_Timestamp expire; // not used -> no expiration, always 0
    108 
    109   /**
    110    * Function to call with the donau's certification data,
    111    * NULL if this has already been done.
    112    */
    113   DONAU_GetKeysCallback cert_cb;
    114 
    115   /**
    116    * Closure to pass to @e cert_cb.
    117    */
    118   void *cert_cb_cls;
    119 
    120 };
    121 
    122 
    123 #define EXITIF(cond)                                              \
    124         do {                                                            \
    125           if (cond) { GNUNET_break (0); goto EXITIF_exit; }             \
    126         } while (0)
    127 
    128 /**
    129  * Parse a donau's signing key encoded in JSON.
    130  *
    131  * @param[out] sign_key where to return the result
    132  * @param sign_key_obj json to parse
    133  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a sign_key_obj
    134  * is malformed.
    135  */
    136 static enum GNUNET_GenericReturnValue
    137 parse_json_signkey (struct DONAU_SigningPublicKeyAndValidity *sign_key,
    138                     const json_t *sign_key_obj)
    139 {
    140   struct GNUNET_JSON_Specification spec[] = {
    141     GNUNET_JSON_spec_fixed_auto ("key",
    142                                  &sign_key->key),
    143     GNUNET_JSON_spec_timestamp ("stamp_start",
    144                                 &sign_key->valid_from),
    145     GNUNET_JSON_spec_timestamp ("stamp_expire",
    146                                 &sign_key->expire_sign),
    147     GNUNET_JSON_spec_end ()
    148   };
    149 
    150   if (GNUNET_OK !=
    151       GNUNET_JSON_parse (sign_key_obj,
    152                          spec,
    153                          NULL, NULL))
    154   {
    155     GNUNET_break_op (0);
    156     return GNUNET_SYSERR;
    157   }
    158   return GNUNET_OK;
    159 }
    160 
    161 
    162 /**
    163  * Parse a donau's donation unit key encoded in JSON.
    164  *
    165  * @param[out] du where to return the result
    166  * @param donation_unit_obj json to parse
    167  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a donation_unit_obj
    168  * is malformed.
    169  */
    170 static enum GNUNET_GenericReturnValue
    171 parse_json_donation_unit (struct DONAU_DonationUnitInformation *du,
    172                           const json_t *donation_unit_obj)
    173 {
    174   struct GNUNET_JSON_Specification spec[] = {
    175     DONAU_JSON_spec_donation_unit_pub ("donation_unit_pub",
    176                                        &du->key),
    177     GNUNET_JSON_spec_uint64 ("year",
    178                              &du->year),
    179     GNUNET_JSON_spec_bool ("lost",
    180                            &du->lost),
    181     TALER_JSON_spec_amount_any ("value",
    182                                 &du->value),
    183     GNUNET_JSON_spec_end ()
    184   };
    185 
    186   if (GNUNET_OK !=
    187       GNUNET_JSON_parse (donation_unit_obj,
    188                          spec,
    189                          NULL, NULL))
    190   {
    191     GNUNET_break_op (0);
    192     return GNUNET_SYSERR;
    193   }
    194 
    195   return GNUNET_OK;
    196 }
    197 
    198 
    199 /**
    200  * Decode the JSON in @a resp_obj from the /keys response
    201  * and store the data in the @a key_data.
    202  *
    203  * @param[in] resp_obj JSON object to parse
    204  * @param[out] key_data where to store the results we decoded
    205  * @param[out] vc where to store version compatibility data
    206  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
    207  * (malformed JSON)
    208  */
    209 static enum GNUNET_GenericReturnValue
    210 decode_keys_json (const json_t *resp_obj,
    211                   struct DONAU_Keys *key_data,
    212                   enum DONAU_VersionCompatibility *vc)
    213 {
    214   const json_t *sign_keys_array;
    215   const json_t *donation_units_array;
    216 
    217   if (JSON_OBJECT != json_typeof (resp_obj))
    218   {
    219     GNUNET_break_op (0);
    220     return GNUNET_SYSERR;
    221   }
    222 #if DEBUG
    223   json_dumpf (resp_obj,
    224               stderr,
    225               JSON_INDENT (2));
    226 #endif
    227   /* check the version first */
    228   {
    229     const char *ver;
    230     unsigned int age;
    231     unsigned int revision;
    232     unsigned int current;
    233     char dummy;
    234     struct GNUNET_JSON_Specification spec[] = {
    235       GNUNET_JSON_spec_string ("version",
    236                                &ver),
    237       GNUNET_JSON_spec_end ()
    238     };
    239 
    240     if (GNUNET_OK !=
    241         GNUNET_JSON_parse (resp_obj,
    242                            spec,
    243                            NULL, NULL))
    244     {
    245       GNUNET_break_op (0);
    246       return GNUNET_SYSERR;
    247     }
    248     if (3 != sscanf (ver,
    249                      "%u:%u:%u%c",
    250                      &current,
    251                      &revision,
    252                      &age,
    253                      &dummy))
    254     {
    255       GNUNET_break_op (0);
    256       return GNUNET_SYSERR;
    257     }
    258     *vc = DONAU_VC_MATCH; // 0
    259     if (DONAU_PROTOCOL_CURRENT < current)
    260     {
    261       *vc |= DONAU_VC_NEWER; // 4
    262       if (DONAU_PROTOCOL_CURRENT < current - age)
    263         *vc |= DONAU_VC_INCOMPATIBLE; // 1
    264     }
    265     if (DONAU_PROTOCOL_CURRENT > current)
    266     {
    267       *vc |= DONAU_VC_OLDER; // 2
    268       if (DONAU_PROTOCOL_CURRENT - DONAU_PROTOCOL_AGE > current)
    269         *vc |= DONAU_VC_INCOMPATIBLE; // 1
    270     }
    271     key_data->version = GNUNET_strdup (ver);
    272   }
    273 
    274   {
    275     const char *currency;
    276     struct GNUNET_JSON_Specification mspec[] = {
    277       GNUNET_JSON_spec_array_const (
    278         "signkeys",
    279         &sign_keys_array),
    280       GNUNET_JSON_spec_string (
    281         "currency",
    282         &currency),
    283       GNUNET_JSON_spec_array_const (
    284         "donation_units",
    285         &donation_units_array),
    286       GNUNET_JSON_spec_end ()
    287     };
    288     const char *emsg;
    289     unsigned int eline;
    290 
    291     if (GNUNET_OK !=
    292         GNUNET_JSON_parse (resp_obj,
    293                            mspec,
    294                            &emsg,
    295                            &eline))
    296     {
    297       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    298                   "Parsing /keys failed for `%s' (%u)\n",
    299                   emsg,
    300                   eline);
    301       EXITIF (1);
    302     }
    303 
    304     key_data->currency = GNUNET_strdup (currency);
    305   }
    306 
    307   /* parse the signing keys */
    308   key_data->num_sign_keys
    309     = json_array_size (sign_keys_array);
    310   if (0 != key_data->num_sign_keys)
    311   {
    312     json_t *sign_key_obj;
    313     unsigned int index;
    314 
    315     key_data->sign_keys
    316       = GNUNET_new_array (key_data->num_sign_keys,
    317                           struct DONAU_SigningPublicKeyAndValidity);
    318     json_array_foreach (sign_keys_array, index, sign_key_obj) {
    319       EXITIF (GNUNET_SYSERR ==
    320               parse_json_signkey (&key_data->sign_keys[index],
    321                                   sign_key_obj));
    322     }
    323   }
    324 
    325   /*
    326    * Parse the donation unit keys
    327    */
    328   key_data->num_donation_unit_keys
    329     = json_array_size (donation_units_array);
    330   if (0 != key_data->num_donation_unit_keys)
    331   {
    332     json_t *donation_unit_obj;
    333     size_t index;
    334 
    335     key_data->donation_unit_keys
    336       = GNUNET_new_array (key_data->num_donation_unit_keys,
    337                           struct DONAU_DonationUnitInformation);
    338     json_array_foreach (donation_units_array, index, donation_unit_obj) {
    339       EXITIF (GNUNET_SYSERR ==
    340               parse_json_donation_unit (&key_data->donation_unit_keys[index],
    341                                         donation_unit_obj));
    342     }
    343   }
    344 
    345   return GNUNET_OK;
    346 
    347 EXITIF_exit:
    348   *vc = DONAU_VC_PROTOCOL_ERROR;
    349   return GNUNET_SYSERR;
    350 }
    351 
    352 
    353 /**
    354  * Callback used when downloading the reply to a /keys request
    355  * is complete.
    356  *
    357  * @param cls the `struct KeysRequest`
    358  * @param response_code HTTP response code, 0 on error
    359  * @param resp_obj parsed JSON result, NULL on error
    360  */
    361 static void
    362 keys_completed_cb (void *cls,
    363                    long response_code,
    364                    const void *resp_obj)
    365 {
    366   struct DONAU_GetKeysHandle *gkh = cls;
    367   const json_t *j = resp_obj;
    368   struct DONAU_Keys *kd = NULL;
    369   struct DONAU_KeysResponse kresp = {
    370     .hr.reply = j,
    371     .hr.http_status = (unsigned int) response_code,
    372     .details.ok.compat = DONAU_VC_PROTOCOL_ERROR
    373   };
    374 
    375   gkh->job = NULL;
    376   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    377               "Received keys from URL `%s' with status %ld.\n",
    378               gkh->url,
    379               response_code);
    380   switch (response_code)
    381   {
    382   case 0:
    383     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    384                 "Failed to receive /keys response from donau %s\n",
    385                 gkh->donau_url);
    386     break;
    387   case MHD_HTTP_OK:
    388     if (NULL == j)
    389     {
    390       GNUNET_break (0);
    391       response_code = 0;
    392       break;
    393     }
    394     kd = GNUNET_new (struct DONAU_Keys);
    395     kd->donau_url = GNUNET_strdup (gkh->donau_url);
    396 
    397     if (GNUNET_OK !=
    398         decode_keys_json (j,
    399                           kd,
    400                           &kresp.details.ok.compat))
    401     {
    402       TALER_LOG_ERROR ("Could not decode /keys response\n");
    403       kd->rc = 1;
    404       DONAU_keys_decref (kd);
    405       kd = NULL;
    406       kresp.hr.http_status = 0;
    407       kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    408       break;
    409     }
    410     kd->rc = 1;
    411 
    412     kresp.details.ok.keys = kd;
    413     break;
    414   case MHD_HTTP_BAD_REQUEST:
    415   case MHD_HTTP_UNAUTHORIZED:
    416   case MHD_HTTP_FORBIDDEN:
    417   case MHD_HTTP_NOT_FOUND:
    418     if (NULL == j)
    419     {
    420       kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    421       kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
    422     }
    423     else
    424     {
    425       kresp.hr.ec = TALER_JSON_get_error_code (j);
    426       kresp.hr.hint = TALER_JSON_get_error_hint (j);
    427     }
    428     break;
    429   default:
    430     if (NULL == j)
    431     {
    432       kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    433       kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
    434     }
    435     else
    436     {
    437       kresp.hr.ec = TALER_JSON_get_error_code (j);
    438       kresp.hr.hint = TALER_JSON_get_error_hint (j);
    439     }
    440     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    441                 "Unexpected response code %u/%d\n",
    442                 (unsigned int) response_code,
    443                 (int) kresp.hr.ec);
    444     break;
    445   }
    446   gkh->cert_cb (gkh->cert_cb_cls,
    447                 &kresp,
    448                 kd);
    449   DONAU_get_keys_cancel (gkh);
    450 }
    451 
    452 
    453 struct DONAU_GetKeysHandle *
    454 DONAU_get_keys (
    455   struct GNUNET_CURL_Context *ctx,
    456   const char *url,
    457   DONAU_GetKeysCallback cert_cb,
    458   void *cert_cb_cls)
    459 {
    460   struct DONAU_GetKeysHandle *gkh;
    461   CURL *eh;
    462 
    463   TALER_LOG_DEBUG ("Connecting to the donau (%s)\n",
    464                    url);
    465   gkh = GNUNET_new (struct DONAU_GetKeysHandle);
    466   gkh->donau_url = GNUNET_strdup (url);
    467   gkh->cert_cb = cert_cb;
    468   gkh->cert_cb_cls = cert_cb_cls;
    469   gkh->url = TALER_url_join (url,
    470                              "keys",
    471                              NULL);
    472   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    473               "Requesting keys with URL `%s'.\n",
    474               gkh->url);
    475   eh = DONAU_curl_easy_get_ (gkh->url);
    476   if (NULL == eh)
    477   {
    478     GNUNET_break (0);
    479     GNUNET_free (gkh->donau_url);
    480     GNUNET_free (gkh->url);
    481     GNUNET_free (gkh);
    482     return NULL;
    483   }
    484   GNUNET_break (CURLE_OK ==
    485                 curl_easy_setopt (eh,
    486                                   CURLOPT_VERBOSE,
    487                                   0));
    488   GNUNET_break (CURLE_OK ==
    489                 curl_easy_setopt (eh,
    490                                   CURLOPT_TIMEOUT,
    491                                   120 /* seconds */));
    492   gkh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
    493                                                eh,
    494                                                &keys_completed_cb,
    495                                                gkh);
    496   return gkh;
    497 }
    498 
    499 
    500 void
    501 DONAU_get_keys_cancel (
    502   struct DONAU_GetKeysHandle *gkh)
    503 {
    504   if (NULL != gkh->job)
    505   {
    506     GNUNET_CURL_job_cancel (gkh->job);
    507     gkh->job = NULL;
    508   }
    509   // DONAU_keys_decref (gkh->prev_keys);
    510   GNUNET_free (gkh->donau_url);
    511   GNUNET_free (gkh->url);
    512   GNUNET_free (gkh);
    513 }
    514 
    515 
    516 const struct DONAU_DonationUnitInformation *
    517 DONAU_get_donation_unit_key (
    518   const struct DONAU_Keys *keys,
    519   const struct DONAU_DonationUnitPublicKey *pk)
    520 {
    521   for (unsigned int i = 0; i<keys->num_donation_unit_keys; i++)
    522     if (0 ==
    523         DONAU_donation_unit_pub_cmp (pk,
    524                                      &keys->donation_unit_keys[i].key))
    525       return &keys->donation_unit_keys[i];
    526   return NULL;
    527 }
    528 
    529 
    530 const struct DONAU_DonationUnitInformation *
    531 DONAU_get_donation_unit_key_by_hash (
    532   const struct DONAU_Keys *keys,
    533   const struct DONAU_DonationUnitHashP *hc)
    534 {
    535   for (unsigned int i = 0; i<keys->num_donation_unit_keys; i++)
    536     // memcmp needs two pointer of the same type
    537     if (0 == GNUNET_memcmp (&hc->hash,
    538                             &keys->donation_unit_keys[i].key.bsign_pub_key->
    539                             pub_key_hash))
    540       return &keys->donation_unit_keys[i];
    541   return NULL;
    542 }
    543 
    544 
    545 bool
    546 DONAU_compute_salted_tax_id_hash (const char *donor_tax_id,
    547                                   const char *salt,
    548                                   unsigned char out_hash[512 / 8])
    549 {
    550   crypto_hash_sha512_state st;
    551 
    552   if ( (NULL == donor_tax_id) ||
    553        (NULL == salt) ||
    554        (NULL == out_hash) )
    555   {
    556     GNUNET_break (0);
    557     return false;
    558   }
    559   /* FIXME: use GNUnet wrapper */
    560   crypto_hash_sha512_init (&st);
    561   crypto_hash_sha512_update (&st,
    562                              (const unsigned char *) donor_tax_id,
    563                              strlen (donor_tax_id) + 1);
    564   crypto_hash_sha512_update (&st,
    565                              (const unsigned char *) salt,
    566                              strlen (salt) + 1);
    567   crypto_hash_sha512_final (&st,
    568                             out_hash);
    569   return true;
    570 }
    571 
    572 
    573 /**
    574  * Local helper for the #DONAU_select_donation_unit_keys_for_amount()
    575  */
    576 struct DUEntry
    577 {
    578   struct TALER_Amount value;
    579   const struct DONAU_DonationUnitPublicKey *pub; /* points into keys */
    580 };
    581 
    582 /**
    583  * Small helper function for sorting
    584  */
    585 static int
    586 du_amount_desc_cmp (const void *a,
    587                     const void *b)
    588 {
    589   const struct DUEntry *ea = a;
    590   const struct DUEntry *eb = b;
    591 
    592   int c = TALER_amount_cmp (&ea->value,
    593                             &eb->value);
    594   /* Descending */
    595   return (c < 0) ? 1 : (c > 0 ? -1 : 0);
    596 }
    597 
    598 
    599 enum GNUNET_GenericReturnValue
    600 DONAU_select_donation_unit_keys_for_amount (
    601   const struct DONAU_Keys *keys,
    602   const struct TALER_Amount *requested_amount,
    603   uint64_t year,
    604   struct DONAU_DonationUnitPublicKey **out_keys,
    605   uint32_t *out_len)
    606 {
    607   struct DONAU_DonationUnitPublicKey *result = NULL;
    608   uint32_t result_len = 0;
    609   struct TALER_Amount remaining, zero;
    610   struct DUEntry *duv = NULL;
    611   unsigned int n_duv = 0;
    612   unsigned int i = 0;
    613 
    614   if ( (NULL == keys) ||
    615        (NULL == requested_amount) ||
    616        (NULL == out_keys) ||
    617        (NULL == out_len) )
    618   {
    619     GNUNET_break (0);
    620     return GNUNET_SYSERR;
    621   }
    622 
    623   if (0 != strcasecmp (keys->currency,
    624                        requested_amount->currency))
    625   {
    626     GNUNET_break (0);
    627     return GNUNET_SYSERR;
    628   }
    629 
    630   remaining = *requested_amount;
    631   TALER_amount_set_zero (keys->currency,
    632                          &zero);
    633 
    634   if (0 == TALER_amount_cmp (&remaining,
    635                              &zero))
    636   {
    637     *out_keys = NULL;
    638     *out_len = 0;
    639     return GNUNET_OK;
    640   }
    641 
    642   /* Build and sort (desc) the eligible units */
    643   for (unsigned int j = 0;
    644        j < keys->num_donation_unit_keys;
    645        j++)
    646   {
    647     const struct DONAU_DonationUnitInformation *du
    648       = &keys->donation_unit_keys[j];
    649 
    650     if (du->lost)
    651       continue;
    652     if (du->year != year)
    653       continue;
    654 
    655     GNUNET_array_grow (duv,
    656                        n_duv,
    657                        n_duv + 1);
    658     duv[n_duv - 1].value = du->value;
    659     duv[n_duv - 1].pub = &du->key;
    660   }
    661 
    662   if (0 == n_duv)
    663   {
    664     *out_keys = NULL;
    665     *out_len = 0;
    666     return GNUNET_NO;
    667   }
    668 
    669   qsort (duv,
    670          n_duv,
    671          sizeof(struct DUEntry),
    672          &du_amount_desc_cmp);
    673 
    674   while (i < n_duv)
    675   {
    676     int cmp = TALER_amount_cmp (&duv[i].value,
    677                                 &remaining);
    678 
    679     if (cmp <= 0)
    680     {
    681       /* Take as many as we can of duv[i] without overshooting */
    682       for (;;)
    683       {
    684         struct TALER_Amount tmp;
    685         int rc;
    686 
    687         if (TALER_amount_cmp (&duv[i].value,
    688                               &remaining) > 0)
    689           break;
    690 
    691         GNUNET_array_append (result,
    692                              result_len,
    693                              *duv[i].pub);
    694 
    695         rc = TALER_amount_subtract (&tmp,
    696                                     &remaining,
    697                                     &duv[i].value);
    698 
    699         if (TALER_AAR_RESULT_ZERO == rc)
    700         {
    701           remaining = tmp; /* zero */
    702           GNUNET_free (duv);
    703           *out_keys = result;
    704           *out_len  = result_len;
    705           return GNUNET_OK; /* exact match */
    706         }
    707         if (TALER_AAR_RESULT_POSITIVE == rc)
    708         {
    709           remaining = tmp; /* keep taking this same value */
    710           continue;
    711         }
    712 
    713         /* Shouldn't happen because we guard with cmp <= 0 */
    714         GNUNET_break (0);
    715         GNUNET_free (duv);
    716         GNUNET_array_grow (result, result_len, 0);
    717         *out_keys = NULL;
    718         *out_len  = 0;
    719         return GNUNET_SYSERR;
    720       }
    721       /* current no longer fits, move to next smaller */
    722       i++;
    723       continue;
    724     }
    725     i++;
    726   }
    727 
    728   /* No exact combination found */
    729   GNUNET_free (duv);
    730   GNUNET_array_grow (result, result_len, 0);
    731   *out_keys = NULL;
    732   *out_len  = 0;
    733   return GNUNET_NO;
    734 }
    735 
    736 
    737 enum GNUNET_GenericReturnValue
    738 DONAU_get_donation_amount_from_bkps (
    739   const struct DONAU_Keys *keys,
    740   const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps,
    741   size_t num_bkps,
    742   uint64_t year,
    743   struct TALER_Amount *sum_out)
    744 {
    745   /* Sanity-checks */
    746   if (NULL == bkps)
    747   {
    748     GNUNET_break (0);
    749     return GNUNET_NO;
    750   }
    751   if (0 == num_bkps)
    752   {
    753     GNUNET_break (0);
    754     return GNUNET_NO;
    755   }
    756 
    757   TALER_amount_set_zero (keys->currency,
    758                          sum_out);
    759   if (GNUNET_YES ==
    760       DONAU_check_bkps_duplication (bkps,
    761                                     num_bkps))
    762   {
    763     GNUNET_break (0);
    764     return GNUNET_NO;
    765   }
    766 
    767   for (size_t i = 0; i < num_bkps; i++)
    768   {
    769     const struct DONAU_DonationUnitInformation *dui =
    770       DONAU_get_donation_unit_key_by_hash (keys,
    771                                            &bkps[i].h_donation_unit_pub);
    772 
    773     if (NULL == dui)
    774     {
    775       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    776                   "Donation unit public key unknown\n");
    777       return GNUNET_NO;
    778     }
    779     if (dui->year != year)
    780     {
    781       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    782                   "Donation sum for year %lu requested, but "
    783                   "donation unit is for year %lu\n",
    784                   (unsigned long) year,
    785                   (unsigned long) dui->year);
    786       return GNUNET_NO;
    787     }
    788 
    789     switch (TALER_amount_add (sum_out,
    790                               sum_out,
    791                               &dui->value))
    792     {
    793     case TALER_AAR_RESULT_POSITIVE:
    794     case TALER_AAR_RESULT_ZERO:
    795       /* Zero a bit strange, but let's say that it's fine to some extent */
    796       break;
    797 
    798     case TALER_AAR_INVALID_NEGATIVE_RESULT:
    799       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    800                   "Accumulation would go negative\n");
    801       GNUNET_break (0);
    802       return GNUNET_SYSERR;
    803 
    804     case TALER_AAR_INVALID_RESULT_OVERFLOW:
    805       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    806                   "Accumulation overflow\n");
    807       GNUNET_break (0);
    808       return GNUNET_SYSERR;
    809 
    810     case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    811       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    812                   "Normalization failed during add\n");
    813       GNUNET_break (0);
    814       return GNUNET_SYSERR;
    815 
    816     case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    817       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    818                   "Currency mismatch during add\n");
    819       GNUNET_break (0);
    820       return GNUNET_SYSERR;
    821     }
    822   }
    823   return GNUNET_OK;
    824 }
    825 
    826 
    827 bool
    828 DONAU_check_bkps_duplication (
    829   const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps,
    830   const size_t num_bkps)
    831 {
    832   if ( (NULL == bkps) ||
    833        (num_bkps < 2) )
    834     return GNUNET_NO;
    835 
    836   for (size_t i = 0; i < num_bkps - 1; i++)
    837     for (size_t j = i + 1; j < num_bkps; j++)
    838       if (0 == GNUNET_memcmp (&bkps[i].blinded_udi,
    839                               &bkps[j].blinded_udi))
    840         return GNUNET_YES;
    841 
    842   return GNUNET_NO;
    843 }
    844 
    845 
    846 struct DONAU_Keys *
    847 DONAU_keys_incref (struct DONAU_Keys *keys)
    848 {
    849   GNUNET_assert (keys->rc < UINT_MAX);
    850   keys->rc++;
    851   return keys;
    852 }
    853 
    854 
    855 void
    856 DONAU_keys_decref (struct DONAU_Keys *keys)
    857 {
    858   if (NULL == keys)
    859     return;
    860   GNUNET_assert (0 < keys->rc);
    861   keys->rc--;
    862   if (0 != keys->rc)
    863     return;
    864   GNUNET_array_grow (keys->sign_keys,
    865                      keys->num_sign_keys,
    866                      0);
    867   for (unsigned int i = 0; i<keys->num_donation_unit_keys; i++)
    868     DONAU_donation_unit_pub_free (&keys->donation_unit_keys[i].key);
    869 
    870   GNUNET_array_grow (keys->donation_unit_keys,
    871                      keys->donation_unit_keys_size,
    872                      0);
    873   GNUNET_free (keys->version);
    874   GNUNET_free (keys->currency);
    875   GNUNET_free (keys->donau_url);
    876   GNUNET_free (keys);
    877 }
    878 
    879 
    880 struct DONAU_Keys *
    881 DONAU_keys_from_json (const json_t *j)
    882 {
    883   const json_t *jkeys;
    884   const char *url;
    885   uint32_t version;
    886   struct GNUNET_JSON_Specification spec[] = {
    887     GNUNET_JSON_spec_uint32 ("version",
    888                              &version),
    889     GNUNET_JSON_spec_object_const ("keys",
    890                                    &jkeys),
    891     GNUNET_JSON_spec_string ("donau_url",
    892                              &url),
    893     GNUNET_JSON_spec_end ()
    894   };
    895   struct DONAU_Keys *keys;
    896   enum DONAU_VersionCompatibility compat;
    897 
    898   if (NULL == j)
    899     return NULL;
    900   if (GNUNET_OK !=
    901       GNUNET_JSON_parse (j,
    902                          spec,
    903                          NULL, NULL))
    904   {
    905     GNUNET_break_op (0);
    906     return NULL;
    907   }
    908   if (0 != version)
    909   {
    910     return NULL; /* unsupported version */
    911   }
    912   keys = GNUNET_new (struct DONAU_Keys);
    913   if (GNUNET_OK !=
    914       decode_keys_json (jkeys,
    915                         keys,
    916                         &compat))
    917   {
    918     GNUNET_break (0);
    919     return NULL;
    920   }
    921   keys->rc = 1;
    922   keys->donau_url = GNUNET_strdup (url);
    923   return keys;
    924 }
    925 
    926 
    927 /**
    928  * Data we track per donation unit group.
    929  */
    930 struct GroupData
    931 {
    932   /**
    933    * The json blob with the group meta-data and list of donation units
    934    */
    935   json_t *json;
    936 
    937   /**
    938    * Meta data for this group.
    939    */
    940   struct DONAU_DonationUnitGroup meta;
    941 };
    942 
    943 
    944 json_t *
    945 DONAU_keys_to_json (const struct DONAU_Keys *kd)
    946 {
    947   // --- Create the array of signkeys (unchanged) ---
    948   json_t *signkeys = json_array ();
    949   json_t *keys;
    950   json_t *donation_units;
    951   json_t *currency_spec = NULL;
    952 
    953   GNUNET_assert (NULL != signkeys);
    954   for (unsigned int i = 0; i < kd->num_sign_keys; i++)
    955   {
    956     const struct DONAU_SigningPublicKeyAndValidity *sk = &kd->sign_keys[i];
    957     json_t *signkey;
    958 
    959     signkey = GNUNET_JSON_PACK (
    960       GNUNET_JSON_pack_data_auto ("key",
    961                                   &sk->key),
    962       GNUNET_JSON_pack_timestamp ("stamp_start",
    963                                   sk->valid_from),
    964       GNUNET_JSON_pack_timestamp ("stamp_expire",
    965                                   sk->expire_sign));
    966     GNUNET_assert (NULL != signkey);
    967     GNUNET_assert (0 ==
    968                    json_array_append_new (signkeys,
    969                                           signkey));
    970   }
    971 
    972   // --- Create the array of donation_units ---
    973   donation_units = json_array ();
    974   GNUNET_assert (NULL != donation_units);
    975   for (unsigned int i = 0; i < kd->num_donation_unit_keys; i++)
    976   {
    977     const struct DONAU_DonationUnitInformation *du = &kd->donation_unit_keys[i];
    978 
    979     // Build the JSON for one donation unit
    980     json_t *donation_unit_obj = GNUNET_JSON_PACK (
    981       DONAU_JSON_pack_donation_unit_pub ("donation_unit_pub", &du->key),
    982       GNUNET_JSON_pack_uint64 ("year", du->year),
    983       GNUNET_JSON_pack_bool ("lost", du->lost),
    984       TALER_JSON_pack_amount ("value", &du->value)
    985       );
    986 
    987     GNUNET_assert (NULL != donation_unit_obj);
    988     GNUNET_assert (0 ==
    989                    json_array_append_new (donation_units,
    990                                           donation_unit_obj));
    991   }
    992 
    993   if (NULL != kd->currency_specification.name)
    994   {
    995     currency_spec = TALER_JSON_currency_specs_to_json (
    996       &kd->currency_specification
    997       );
    998   }
    999 
   1000   keys = GNUNET_JSON_PACK (
   1001     GNUNET_JSON_pack_string ("version",
   1002                              kd->version),
   1003     GNUNET_JSON_pack_string ("currency",
   1004                              kd->currency),
   1005     GNUNET_JSON_pack_array_steal ("signkeys",
   1006                                   signkeys),
   1007     GNUNET_JSON_pack_array_steal ("donation_units",
   1008                                   donation_units)
   1009     );
   1010 
   1011   if (NULL != currency_spec)
   1012   {
   1013     json_object_set_new (keys,
   1014                          "currency_specification",
   1015                          currency_spec);
   1016   }
   1017 
   1018   return GNUNET_JSON_PACK (
   1019     GNUNET_JSON_pack_uint64 ("version",
   1020                              DONAU_SERIALIZATION_FORMAT_VERSION),
   1021     GNUNET_JSON_pack_string ("donau_url",
   1022                              kd->donau_url),
   1023     GNUNET_JSON_pack_object_steal ("keys",
   1024                                    keys));
   1025 }
   1026 
   1027 
   1028 /* end of donau_api_handle.c */