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