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