testing_api_cmd_issue_receipts.c (19155B)
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 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your 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 * @file testing/testing_api_cmd_issue_receipts.c 21 * @brief Implement the POST /charities test command. 22 * @author Lukas Matyja 23 */ 24 #include <donau_config.h> 25 #include <taler/taler_json_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include <taler/taler_testing_lib.h> 28 struct StatusState; 29 struct CSR_Data; 30 #define DONAU_BATCH_ISSUE_RECEIPTS_RESULT_CLOSURE struct StatusState 31 #define DONAU_CSR_BATCH_ISSUE_RESULT_CLOSURE struct CSR_Data 32 #include "donau_testing_lib.h" 33 34 35 /** 36 * State for a "status" CMD. 37 */ 38 struct StatusState 39 { 40 /** 41 * Handle to the "batch issue receipt status" operation. 42 */ 43 struct DONAU_BatchIssueReceiptHandle *birh; 44 45 /** 46 * Reference to charity post command. 47 */ 48 const char *charity_reference; 49 50 /** 51 * Issue amount 52 */ 53 struct TALER_Amount amount; 54 55 /** 56 * Expected HTTP response code. 57 */ 58 unsigned int expected_response_code; 59 60 /** 61 */ 62 bool uses_cs; 63 64 /** 65 * Interpreter state. 66 */ 67 struct TALER_TESTING_Interpreter *is; 68 69 /** 70 * charity id 71 */ 72 uint64_t charity_id; 73 74 /** 75 * charity id 76 */ 77 uint32_t year; 78 79 /** 80 * Private key of the charity, for signature. 81 */ 82 struct DONAU_CharityPrivateKeyP charity_priv; 83 84 /** 85 * number of budi key pair. 86 */ 87 size_t num_bkp; 88 89 /** 90 * budi key pair array 91 */ 92 struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps; 93 94 /** 95 * donau keys 96 */ 97 struct DONAU_Keys *keys; 98 99 /** 100 * The cleartext tax id of the user used for @h_donor_tax_id. 101 */ 102 const char *donor_tax_id; 103 104 /** 105 * The salt used for @h_donor_tax_id. 106 */ 107 const char *donor_salt; 108 109 /** 110 * Hashed and salted tax id of the donor. 111 */ 112 struct DONAU_HashDonorTaxId h_donor_tax_id; 113 114 /** 115 * Selected donation-unit pubkeys for this request 116 */ 117 struct DONAU_DonationUnitPublicKey *selected_pks; 118 119 /** 120 * Array of donation receipts; 121 */ 122 struct DONAU_DonationReceipt *receipts; 123 124 /** 125 * Blinding secrets 126 */ 127 union GNUNET_CRYPTO_BlindingSecretP *blinding_secrets; 128 129 /** 130 * Blinding values. Cs-nonces, cipher. 131 */ 132 struct DONAU_BatchIssueValues **alg_values; 133 134 /** 135 * Array of hashed udis. 136 */ 137 struct DONAU_UniqueDonorIdentifierHashP *h_udis; 138 139 /** 140 * Number of pending CS requests. 141 */ 142 size_t cs_pending; 143 }; 144 145 146 struct CSR_Data 147 { 148 /** 149 * Handle to the "batch issue receipt status" operation. 150 */ 151 struct DONAU_CsRBatchIssueHandle *csr_handle; 152 153 /** 154 * CS-Nonce 155 */ 156 union GNUNET_CRYPTO_BlindSessionNonce nonce; 157 158 /** 159 * batch issue receipt status state 160 */ 161 struct StatusState *ss; 162 163 /** 164 * array position in batch issue receipt request (first position is zero) 165 */ 166 size_t position; 167 }; 168 169 170 /** 171 * Check that the reserve balance and HTTP response code are 172 * both acceptable. 173 * 174 * @param ss closure. 175 * @param biresp HTTP response details 176 */ 177 static void 178 issue_receipts_status_cb (struct StatusState *ss, 179 const struct DONAU_BatchIssueResponse *biresp) 180 { 181 ss->birh = NULL; 182 if (ss->expected_response_code != biresp->hr.http_status) 183 { 184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 185 "Unexpected HTTP response code: %d in %s:%u\n", 186 biresp->hr.http_status, 187 __FILE__, 188 __LINE__); 189 json_dumpf (biresp->hr.reply, 190 stderr, 191 0); 192 TALER_TESTING_interpreter_fail (ss->is); 193 return; 194 } 195 if (MHD_HTTP_OK != biresp->hr.http_status) 196 { 197 TALER_TESTING_interpreter_next (ss->is); 198 return; 199 } 200 { 201 struct DONAU_BlindedDonationUnitSignature *blinded_sigs = 202 biresp->details.ok.blinded_sigs; 203 if (biresp->details.ok.num_blinded_sigs != ss->num_bkp) 204 { 205 GNUNET_break (0); 206 TALER_TESTING_interpreter_fail (ss->is); 207 return; 208 } 209 for (size_t i = 0; i < ss->num_bkp; i++) 210 { 211 struct DONAU_UniqueDonorIdentifierHashP checkudi_hash; 212 213 GNUNET_assert (GNUNET_OK == 214 DONAU_donation_unit_sig_unblind ( 215 &ss->receipts[i].donation_unit_sig, 216 &blinded_sigs[i], 217 &ss->blinding_secrets[i], 218 &ss->h_udis[i], 219 ss->alg_values[i], 220 &ss->selected_pks[i])); 221 222 /* check udi message */ 223 DONAU_unique_donor_id_hash ( 224 &ss->h_donor_tax_id, 225 &ss->receipts[i].nonce, 226 &checkudi_hash); 227 GNUNET_assert (0 == GNUNET_CRYPTO_hash_cmp (&checkudi_hash.hash, 228 &ss->h_udis[i].hash)); 229 /* check signature */ 230 if (GNUNET_OK != 231 DONAU_donation_receipt_verify ( 232 &ss->selected_pks[i], 233 &checkudi_hash, 234 &ss->receipts[i].donation_unit_sig)) 235 { 236 GNUNET_break_op (0); 237 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 238 "Donation receipt signature invalid!\n"); 239 TALER_TESTING_interpreter_fail (ss->is); 240 return; 241 } 242 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 243 "Donation receipt signature valid!\n"); 244 } 245 } 246 TALER_TESTING_interpreter_next (ss->is); 247 } 248 249 250 /** 251 * Runs phase two, the actual issue receipts operation. 252 * Started once the preparation for CS-donation-units is 253 * done. 254 * @param cls closure. 255 */ 256 static void 257 phase_two (void *cls) 258 { 259 struct StatusState *ss = cls; 260 const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = ss->bkps; 261 262 ss->birh = DONAU_charity_issue_receipt ( 263 TALER_TESTING_interpreter_get_context (ss->is), 264 TALER_TESTING_get_donau_url (ss->is), 265 &ss->charity_priv, 266 ss->charity_id, 267 ss->year, 268 ss->num_bkp, 269 bkps, 270 &issue_receipts_status_cb, 271 ss); 272 } 273 274 275 /** 276 * Function called when stage 1 of CS issue is finished (request r_pub's) 277 * 278 * @param csr_data the `struct CSR_Data *` 279 * @param csrresp replies from the /csr-issue request 280 */ 281 static void 282 cs_stage_two_callback ( 283 struct CSR_Data *csr_data, 284 const struct DONAU_CsRBatchIssueResponse *csrresp) 285 { 286 struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi = 287 &csr_data->ss->bkps[csr_data->position].blinded_udi; 288 289 if (csrresp->hr.http_status != MHD_HTTP_CREATED) 290 { 291 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 292 "Unexpected HTTP response code: %d in %s:%u\n", 293 csrresp->hr.http_status, 294 __FILE__, 295 __LINE__); 296 json_dumpf (csrresp->hr.reply, 297 stderr, 298 0); 299 TALER_TESTING_interpreter_fail (csr_data->ss->is); 300 GNUNET_free (csr_data); 301 return; 302 } 303 304 { 305 struct DONAU_DonationUnitPublicKey *cs_pk = 306 &csr_data->ss->selected_pks[csr_data->position]; 307 struct DONAU_BatchIssueValues *alg_values 308 = GNUNET_new (struct DONAU_BatchIssueValues); 309 struct DONAU_BudiMasterSecretP ps; 310 struct DONAU_UniqueDonorIdentifierHashP *udi_hash = 311 &csr_data->ss->h_udis[csr_data->position]; 312 union GNUNET_CRYPTO_BlindingSecretP *blinding_secret = 313 &csr_data->ss->blinding_secrets[csr_data->position]; 314 struct DONAU_UniqueDonorIdentifierNonce *udi_nonce = 315 &csr_data->ss->receipts[csr_data->position].nonce; 316 317 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == 318 cs_pk->bsign_pub_key->cipher); 319 320 DONAU_donation_unit_ewv_copy ( 321 alg_values, 322 &csrresp->details.ok.alg_values); 323 GNUNET_CRYPTO_random_block (&ps, 324 sizeof (ps)); 325 DONAU_budi_secret_create (&ps, 326 alg_values, 327 blinding_secret); 328 GNUNET_assert (GNUNET_OK == 329 DONAU_donation_unit_blind ( 330 cs_pk, 331 blinding_secret, 332 &csr_data->nonce, /* nonce only needed for cs */ 333 udi_nonce, 334 &csr_data->ss->h_donor_tax_id, 335 alg_values, 336 udi_hash, 337 blinded_udi)); 338 csr_data->ss->alg_values[csr_data->position] = alg_values; 339 csr_data->ss->cs_pending--; 340 } 341 { 342 struct StatusState *ss = csr_data->ss; 343 344 GNUNET_free (csr_data); 345 if (0 == ss->cs_pending) 346 phase_two (ss); 347 } 348 } 349 350 351 /** 352 * Run the command. 353 * 354 * @param cls closure. 355 * @param cmd the command being executed. 356 * @param is the interpreter state. 357 */ 358 static void 359 status_run (void *cls, 360 const struct TALER_TESTING_Command *cmd, 361 struct TALER_TESTING_Interpreter *is) 362 { 363 struct StatusState *ss = cls; 364 365 (void) cmd; 366 ss->is = is; 367 368 /* Get charity id and the charity private key from trait */ 369 { 370 const struct TALER_TESTING_Command *charity_post_cmd; 371 const uint64_t *charity_id; 372 const struct DONAU_CharityPrivateKeyP *charity_priv; 373 374 375 charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is, 376 ss-> 377 charity_reference); 378 379 if ( (GNUNET_OK != 380 TALER_TESTING_get_trait_charity_id (charity_post_cmd, 381 &charity_id) ) || 382 (GNUNET_OK != 383 TALER_TESTING_get_trait_charity_priv (charity_post_cmd, 384 &charity_priv)) ) 385 { 386 GNUNET_break (0); 387 TALER_TESTING_interpreter_fail (is); 388 return; 389 } 390 ss->charity_id = (uint64_t) *(charity_id); 391 ss->charity_priv = *(charity_priv); 392 } 393 394 /* Get donau keys from trait */ 395 { 396 const struct TALER_TESTING_Command *keys_cmd; 397 struct DONAU_Keys *keys; 398 399 keys_cmd = TALER_TESTING_interpreter_lookup_command (is, 400 "get-donau"); 401 402 if (GNUNET_OK != 403 TALER_TESTING_get_trait_donau_keys (keys_cmd, &keys)) 404 { 405 GNUNET_break (0); 406 TALER_TESTING_interpreter_fail (is); 407 return; 408 } 409 ss->keys = keys; 410 } 411 412 /* Get the donation_unit_keys for requested amount */ 413 { 414 enum GNUNET_GenericReturnValue sret; 415 416 sret = DONAU_select_donation_unit_keys_for_amount ( 417 ss->keys, 418 &ss->amount, 419 ss->year, 420 &ss->selected_pks, 421 &ss->num_bkp); 422 423 if (GNUNET_SYSERR == sret) 424 { 425 GNUNET_break (0); 426 TALER_TESTING_interpreter_fail (is); 427 return; 428 } 429 if ((GNUNET_NO == sret) || (0 == ss->num_bkp)) 430 { 431 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 432 "Could not compose exact amount %s from donation units for %u\n", 433 TALER_amount2s (&ss->amount), 434 (unsigned int) ss->year); 435 TALER_TESTING_interpreter_fail (is); 436 return; 437 } 438 } 439 440 ss->bkps = 441 GNUNET_new_array (ss->num_bkp, 442 struct DONAU_BlindedUniqueDonorIdentifierKeyPair); 443 ss->blinding_secrets = 444 GNUNET_new_array (ss->num_bkp, 445 union GNUNET_CRYPTO_BlindingSecretP); 446 ss->receipts = 447 GNUNET_new_array (ss->num_bkp, 448 struct DONAU_DonationReceipt); 449 ss->alg_values = 450 GNUNET_new_array (ss->num_bkp, 451 struct DONAU_BatchIssueValues *); 452 ss->h_udis = 453 GNUNET_new_array (ss->num_bkp, 454 struct DONAU_UniqueDonorIdentifierHashP); 455 for (size_t cnt = 0; cnt < ss->num_bkp; cnt++) 456 { 457 struct DONAU_UniqueDonorIdentifierNonce *udi_nonce 458 = &ss->receipts[cnt].nonce; 459 struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi 460 = &ss->bkps[cnt].blinded_udi; 461 struct DONAU_UniqueDonorIdentifierHashP *udi_hash 462 = &ss->h_udis[cnt]; 463 struct DONAU_BudiMasterSecretP ps; 464 struct DONAU_BatchIssueValues *alg_values; 465 466 DONAU_donation_unit_pub_hash (&ss->selected_pks[cnt], 467 &ss->bkps[cnt].h_donation_unit_pub); 468 ss->receipts[cnt].h_donation_unit_pub = ss->bkps[cnt].h_donation_unit_pub; 469 GNUNET_CRYPTO_random_block (&ps, 470 sizeof (ps)); 471 GNUNET_CRYPTO_random_block (udi_nonce, 472 sizeof (*udi_nonce)); 473 switch (ss->selected_pks[cnt].bsign_pub_key->cipher) 474 { 475 case GNUNET_CRYPTO_BSA_RSA: 476 alg_values = DONAU_donation_unit_ewv_rsa_singleton (); 477 DONAU_budi_secret_create (&ps, 478 alg_values, 479 &ss->blinding_secrets[cnt]); 480 GNUNET_assert (GNUNET_OK == 481 DONAU_donation_unit_blind ( 482 &ss->selected_pks[cnt], 483 &ss->blinding_secrets[cnt], 484 NULL, /* no cs-nonce needed for rsa */ 485 udi_nonce, 486 &ss->h_donor_tax_id, 487 alg_values, 488 udi_hash, 489 blinded_udi)); 490 ss->alg_values[cnt] = alg_values; 491 break; 492 case GNUNET_CRYPTO_BSA_CS: 493 { 494 struct CSR_Data *csr_data = GNUNET_new (struct CSR_Data); 495 496 // Trying to make it compile after 1e79313 on exchange 497 // TALER_cs_withdraw_nonce_derive ( // FIXME: write new method 498 // (struct TALER_PlanchetMasterSecretP *) &ps, 499 // &csr_data->nonce.cs_nonce); 500 DONAU_cs_issue_nonce_derive (&ps, // FIXME: I assume same fixme as before... 501 &csr_data->nonce.cs_nonce); 502 csr_data->ss = ss; 503 csr_data->position = cnt; 504 csr_data->csr_handle = DONAU_csr_issue ( 505 TALER_TESTING_interpreter_get_context (is), 506 TALER_TESTING_get_donau_url (is), 507 &ss->selected_pks[cnt], 508 &csr_data->nonce.cs_nonce, 509 &cs_stage_two_callback, 510 csr_data); 511 if (NULL == csr_data->csr_handle) 512 { 513 GNUNET_break (0); 514 } 515 ss->cs_pending++; 516 break; 517 } 518 default: 519 GNUNET_break (0); 520 } 521 } 522 if (0 == ss->cs_pending) 523 phase_two (ss); 524 } 525 526 527 /** 528 * Cleanup the state from a "issue receipt status" CMD, and possibly 529 * cancel a pending operation thereof. 530 * 531 * @param cls closure. 532 * @param cmd the command which is being cleaned up. 533 */ 534 static void 535 cleanup (void *cls, 536 const struct TALER_TESTING_Command *cmd) 537 { 538 struct StatusState *ss = cls; 539 540 if (NULL != ss->birh) 541 { 542 // log incomplete command 543 TALER_TESTING_command_incomplete (ss->is, 544 cmd->label); 545 DONAU_charity_issue_receipt_cancel (ss->birh); 546 ss->birh = NULL; 547 } 548 549 GNUNET_free (ss->selected_pks); 550 GNUNET_free (ss->h_udis); 551 if (NULL != ss->alg_values) 552 { 553 /* For CS units, cs_stage_two_callback allocated a per-unit 554 DONAU_BatchIssueValues with an incref'd blinding_inputs; for RSA 555 units the element is the shared static singleton (must not be 556 freed), and a still-pending CS unit leaves a NULL slot. */ 557 for (unsigned int i = 0; i < ss->num_bkp; i++) 558 { 559 struct DONAU_BatchIssueValues *av = ss->alg_values[i]; 560 561 if ( (NULL != av) && 562 (av != DONAU_donation_unit_ewv_rsa_singleton ()) ) 563 { 564 GNUNET_CRYPTO_blinding_input_values_decref ( 565 av->blinding_inputs); 566 GNUNET_free (av); 567 } 568 } 569 } 570 GNUNET_free (ss->alg_values); 571 GNUNET_free (ss->blinding_secrets); 572 if (NULL != ss->receipts) 573 { 574 /* receipts are only signed if the Donau answered 200 */ 575 for (unsigned int i = 0; i < ss->num_bkp; i++) 576 if (NULL != ss->receipts[i].donation_unit_sig.unblinded_sig) 577 GNUNET_CRYPTO_unblinded_sig_decref ( 578 ss->receipts[i].donation_unit_sig.unblinded_sig); 579 GNUNET_free (ss->receipts); 580 } 581 if (NULL != ss->bkps) 582 { 583 for (unsigned int i = 0; i < ss->num_bkp; i++) 584 if (NULL != ss->bkps[i].blinded_udi.blinded_message) 585 GNUNET_CRYPTO_blinded_message_decref ( 586 ss->bkps[i].blinded_udi.blinded_message); 587 GNUNET_free (ss->bkps); 588 } 589 GNUNET_free (ss); 590 } 591 592 593 /** 594 * Offer internal data from a "deposit" CMD, to other commands. 595 * 596 * @param cls closure. 597 * @param[out] ret result. 598 * @param trait name of the trait. 599 * @param index index number of the object to offer. 600 * @return #GNUNET_OK on success. 601 */ 602 static enum GNUNET_GenericReturnValue 603 issue_receipts_traits (void *cls, 604 const void **ret, 605 const char *trait, 606 unsigned int index) 607 { 608 struct StatusState *ss = cls; 609 struct TALER_TESTING_Trait traits[] = { 610 TALER_TESTING_make_trait_donor_salt (ss->donor_salt), 611 TALER_TESTING_make_trait_donor_tax_id (ss->donor_tax_id), 612 TALER_TESTING_make_trait_salted_tax_id_hash ( 613 (const struct DONAU_HashDonorTaxId *) &ss->h_donor_tax_id), 614 TALER_TESTING_make_trait_donation_receipts ( 615 (const struct DONAU_DonationReceipt **) &ss->receipts), 616 TALER_TESTING_make_trait_number_receipts (&ss->num_bkp), 617 TALER_TESTING_trait_end () 618 }; 619 620 return TALER_TESTING_get_trait (traits, 621 ret, 622 trait, 623 index); 624 } 625 626 627 struct TALER_TESTING_Command 628 TALER_TESTING_cmd_issue_receipts (const char *label, 629 const char *charity_reference, 630 bool uses_cs, 631 uint32_t year, 632 const char *donor_tax_id, 633 const char *salt, 634 const char *issue_amount, 635 unsigned int expected_response_code) 636 { 637 struct StatusState *ss; 638 639 ss = GNUNET_new (struct StatusState); 640 ss->year = year; 641 ss->charity_reference = charity_reference; 642 ss->expected_response_code = expected_response_code; 643 ss->uses_cs = uses_cs; 644 ss->donor_salt = (const char*) salt; 645 ss->donor_tax_id = (const char*) donor_tax_id; 646 if (GNUNET_OK != 647 TALER_string_to_amount (issue_amount, 648 &ss->amount)) 649 { 650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 651 "Failed to parse amount `%s' at %s\n", 652 issue_amount, 653 label); 654 GNUNET_assert (0); 655 } 656 657 if (! DONAU_compute_salted_tax_id_hash (donor_tax_id, 658 salt, 659 ss->h_donor_tax_id.hash)) 660 { 661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 662 "Hash was not received"); 663 GNUNET_assert (0); 664 } 665 666 { 667 struct TALER_TESTING_Command cmd = { 668 .cls = ss, 669 .label = label, 670 .run = &status_run, 671 .cleanup = &cleanup, 672 .traits = &issue_receipts_traits 673 }; 674 675 return cmd; 676 } 677 }