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 ¤t, 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 ¤cy), 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 */