donau

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

donau_api_handle.c (27328B)


      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 lifetimes 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     {
    652       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    653                   "Donation unit lost!\n");
    654       continue;
    655     }
    656     if (du->year != year)
    657     {
    658       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    659                   "Donation unit for year %u, want %u!\n",
    660                   (unsigned int) du->year,
    661                   (unsigned int) year);
    662       continue;
    663     }
    664     GNUNET_array_grow (duv,
    665                        n_duv,
    666                        n_duv + 1);
    667     duv[n_duv - 1].value = du->value;
    668     duv[n_duv - 1].pub = &du->key;
    669   }
    670 
    671   if (0 == n_duv)
    672   {
    673     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    674                 "No eligible (non-lost, correct year) donation units found\n");
    675     *out_keys = NULL;
    676     *out_len = 0;
    677     return GNUNET_NO;
    678   }
    679 
    680   qsort (duv,
    681          n_duv,
    682          sizeof(struct DUEntry),
    683          &du_amount_desc_cmp);
    684 
    685   while (i < n_duv)
    686   {
    687     int cmp = TALER_amount_cmp (&duv[i].value,
    688                                 &remaining);
    689 
    690     if (cmp <= 0)
    691     {
    692       /* Take as many as we can of duv[i] without overshooting */
    693       for (;;)
    694       {
    695         struct TALER_Amount tmp;
    696         int rc;
    697 
    698         if (TALER_amount_cmp (&duv[i].value,
    699                               &remaining) > 0)
    700           break;
    701 
    702         GNUNET_array_append (result,
    703                              result_len,
    704                              *duv[i].pub);
    705 
    706         rc = TALER_amount_subtract (&tmp,
    707                                     &remaining,
    708                                     &duv[i].value);
    709 
    710         if (TALER_AAR_RESULT_ZERO == rc)
    711         {
    712           remaining = tmp; /* zero */
    713           GNUNET_free (duv);
    714           *out_keys = result;
    715           *out_len  = result_len;
    716           return GNUNET_OK; /* exact match */
    717         }
    718         if (TALER_AAR_RESULT_POSITIVE == rc)
    719         {
    720           remaining = tmp; /* keep taking this same value */
    721           continue;
    722         }
    723 
    724         /* Shouldn't happen because we guard with cmp <= 0 */
    725         GNUNET_break (0);
    726         GNUNET_free (duv);
    727         GNUNET_array_grow (result, result_len, 0);
    728         *out_keys = NULL;
    729         *out_len  = 0;
    730         return GNUNET_SYSERR;
    731       }
    732       /* current no longer fits, move to next smaller */
    733       i++;
    734       continue;
    735     }
    736     i++;
    737   }
    738 
    739   /* No exact combination found */
    740   GNUNET_free (duv);
    741   GNUNET_array_grow (result, result_len, 0);
    742   *out_keys = NULL;
    743   *out_len  = 0;
    744   return GNUNET_NO;
    745 }
    746 
    747 
    748 enum GNUNET_GenericReturnValue
    749 DONAU_get_donation_amount_from_bkps (
    750   const struct DONAU_Keys *keys,
    751   const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps,
    752   size_t num_bkps,
    753   uint64_t year,
    754   struct TALER_Amount *sum_out)
    755 {
    756   /* Sanity-checks */
    757   if (NULL == bkps)
    758   {
    759     GNUNET_break (0);
    760     return GNUNET_NO;
    761   }
    762   if (0 == num_bkps)
    763   {
    764     GNUNET_break (0);
    765     return GNUNET_NO;
    766   }
    767 
    768   TALER_amount_set_zero (keys->currency,
    769                          sum_out);
    770   if (GNUNET_YES ==
    771       DONAU_check_bkps_duplication (bkps,
    772                                     num_bkps))
    773   {
    774     GNUNET_break (0);
    775     return GNUNET_NO;
    776   }
    777 
    778   for (size_t i = 0; i < num_bkps; i++)
    779   {
    780     const struct DONAU_DonationUnitInformation *dui =
    781       DONAU_get_donation_unit_key_by_hash (keys,
    782                                            &bkps[i].h_donation_unit_pub);
    783 
    784     if (NULL == dui)
    785     {
    786       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    787                   "Donation unit public key unknown\n");
    788       return GNUNET_NO;
    789     }
    790     if (dui->year != year)
    791     {
    792       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    793                   "Donation sum for year %lu requested, but "
    794                   "donation unit is for year %lu\n",
    795                   (unsigned long) year,
    796                   (unsigned long) dui->year);
    797       return GNUNET_NO;
    798     }
    799 
    800     switch (TALER_amount_add (sum_out,
    801                               sum_out,
    802                               &dui->value))
    803     {
    804     case TALER_AAR_RESULT_POSITIVE:
    805     case TALER_AAR_RESULT_ZERO:
    806       /* Zero a bit strange, but let's say that it's fine to some extent */
    807       break;
    808 
    809     case TALER_AAR_INVALID_NEGATIVE_RESULT:
    810       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    811                   "Accumulation would go negative\n");
    812       GNUNET_break (0);
    813       return GNUNET_SYSERR;
    814 
    815     case TALER_AAR_INVALID_RESULT_OVERFLOW:
    816       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    817                   "Accumulation overflow\n");
    818       GNUNET_break (0);
    819       return GNUNET_SYSERR;
    820 
    821     case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    822       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    823                   "Normalization failed during add\n");
    824       GNUNET_break (0);
    825       return GNUNET_SYSERR;
    826 
    827     case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    828       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    829                   "Currency mismatch during add\n");
    830       GNUNET_break (0);
    831       return GNUNET_SYSERR;
    832     }
    833   }
    834   return GNUNET_OK;
    835 }
    836 
    837 
    838 bool
    839 DONAU_check_bkps_duplication (
    840   const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps,
    841   const size_t num_bkps)
    842 {
    843   if ( (NULL == bkps) ||
    844        (num_bkps < 2) )
    845     return GNUNET_NO;
    846 
    847   for (size_t i = 0; i < num_bkps - 1; i++)
    848     for (size_t j = i + 1; j < num_bkps; j++)
    849       if (0 == GNUNET_memcmp (&bkps[i].blinded_udi,
    850                               &bkps[j].blinded_udi))
    851         return GNUNET_YES;
    852 
    853   return GNUNET_NO;
    854 }
    855 
    856 
    857 struct DONAU_Keys *
    858 DONAU_keys_incref (struct DONAU_Keys *keys)
    859 {
    860   GNUNET_assert (keys->rc < UINT_MAX);
    861   keys->rc++;
    862   return keys;
    863 }
    864 
    865 
    866 void
    867 DONAU_keys_decref (struct DONAU_Keys *keys)
    868 {
    869   if (NULL == keys)
    870     return;
    871   GNUNET_assert (0 < keys->rc);
    872   keys->rc--;
    873   if (0 != keys->rc)
    874     return;
    875   GNUNET_array_grow (keys->sign_keys,
    876                      keys->num_sign_keys,
    877                      0);
    878   for (unsigned int i = 0; i<keys->num_donation_unit_keys; i++)
    879     DONAU_donation_unit_pub_free (&keys->donation_unit_keys[i].key);
    880 
    881   GNUNET_array_grow (keys->donation_unit_keys,
    882                      keys->donation_unit_keys_size,
    883                      0);
    884   GNUNET_free (keys->version);
    885   GNUNET_free (keys->currency);
    886   GNUNET_free (keys->donau_url);
    887   GNUNET_free (keys);
    888 }
    889 
    890 
    891 struct DONAU_Keys *
    892 DONAU_keys_from_json (const json_t *j)
    893 {
    894   const json_t *jkeys;
    895   const char *url;
    896   uint32_t version;
    897   struct GNUNET_JSON_Specification spec[] = {
    898     GNUNET_JSON_spec_uint32 ("version",
    899                              &version),
    900     GNUNET_JSON_spec_object_const ("keys",
    901                                    &jkeys),
    902     GNUNET_JSON_spec_string ("donau_url",
    903                              &url),
    904     GNUNET_JSON_spec_end ()
    905   };
    906   struct DONAU_Keys *keys;
    907   enum DONAU_VersionCompatibility compat;
    908 
    909   if (NULL == j)
    910     return NULL;
    911   if (GNUNET_OK !=
    912       GNUNET_JSON_parse (j,
    913                          spec,
    914                          NULL, NULL))
    915   {
    916     GNUNET_break_op (0);
    917     return NULL;
    918   }
    919   if (0 != version)
    920   {
    921     return NULL; /* unsupported version */
    922   }
    923   keys = GNUNET_new (struct DONAU_Keys);
    924   if (GNUNET_OK !=
    925       decode_keys_json (jkeys,
    926                         keys,
    927                         &compat))
    928   {
    929     GNUNET_break (0);
    930     return NULL;
    931   }
    932   keys->rc = 1;
    933   keys->donau_url = GNUNET_strdup (url);
    934   return keys;
    935 }
    936 
    937 
    938 /**
    939  * Data we track per donation unit group.
    940  */
    941 struct GroupData
    942 {
    943   /**
    944    * The json blob with the group meta-data and list of donation units
    945    */
    946   json_t *json;
    947 
    948   /**
    949    * Meta data for this group.
    950    */
    951   struct DONAU_DonationUnitGroup meta;
    952 };
    953 
    954 
    955 json_t *
    956 DONAU_keys_to_json (const struct DONAU_Keys *kd)
    957 {
    958   // --- Create the array of signkeys (unchanged) ---
    959   json_t *signkeys = json_array ();
    960   json_t *keys;
    961   json_t *donation_units;
    962   json_t *currency_spec = NULL;
    963 
    964   GNUNET_assert (NULL != signkeys);
    965   for (unsigned int i = 0; i < kd->num_sign_keys; i++)
    966   {
    967     const struct DONAU_SigningPublicKeyAndValidity *sk = &kd->sign_keys[i];
    968     json_t *signkey;
    969 
    970     signkey = GNUNET_JSON_PACK (
    971       GNUNET_JSON_pack_data_auto ("key",
    972                                   &sk->key),
    973       GNUNET_JSON_pack_timestamp ("stamp_start",
    974                                   sk->valid_from),
    975       GNUNET_JSON_pack_timestamp ("stamp_expire",
    976                                   sk->expire_sign));
    977     GNUNET_assert (NULL != signkey);
    978     GNUNET_assert (0 ==
    979                    json_array_append_new (signkeys,
    980                                           signkey));
    981   }
    982 
    983   // --- Create the array of donation_units ---
    984   donation_units = json_array ();
    985   GNUNET_assert (NULL != donation_units);
    986   for (unsigned int i = 0; i < kd->num_donation_unit_keys; i++)
    987   {
    988     const struct DONAU_DonationUnitInformation *du = &kd->donation_unit_keys[i];
    989 
    990     // Build the JSON for one donation unit
    991     json_t *donation_unit_obj = GNUNET_JSON_PACK (
    992       DONAU_JSON_pack_donation_unit_pub ("donation_unit_pub", &du->key),
    993       GNUNET_JSON_pack_uint64 ("year", du->year),
    994       GNUNET_JSON_pack_bool ("lost", du->lost),
    995       TALER_JSON_pack_amount ("value", &du->value)
    996       );
    997 
    998     GNUNET_assert (NULL != donation_unit_obj);
    999     GNUNET_assert (0 ==
   1000                    json_array_append_new (donation_units,
   1001                                           donation_unit_obj));
   1002   }
   1003 
   1004   if (NULL != kd->currency_specification.name)
   1005   {
   1006     currency_spec = TALER_JSON_currency_specs_to_json (
   1007       &kd->currency_specification
   1008       );
   1009   }
   1010 
   1011   keys = GNUNET_JSON_PACK (
   1012     GNUNET_JSON_pack_string ("version",
   1013                              kd->version),
   1014     GNUNET_JSON_pack_string ("currency",
   1015                              kd->currency),
   1016     GNUNET_JSON_pack_array_steal ("signkeys",
   1017                                   signkeys),
   1018     GNUNET_JSON_pack_array_steal ("donation_units",
   1019                                   donation_units)
   1020     );
   1021 
   1022   if (NULL != currency_spec)
   1023   {
   1024     json_object_set_new (keys,
   1025                          "currency_specification",
   1026                          currency_spec);
   1027   }
   1028 
   1029   return GNUNET_JSON_PACK (
   1030     GNUNET_JSON_pack_uint64 ("version",
   1031                              DONAU_SERIALIZATION_FORMAT_VERSION),
   1032     GNUNET_JSON_pack_string ("donau_url",
   1033                              kd->donau_url),
   1034     GNUNET_JSON_pack_object_steal ("keys",
   1035                                    keys));
   1036 }
   1037 
   1038 
   1039 /* end of donau_api_handle.c */