testing_api_cmd_issue_receipts.c (17857B)
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 unsigned long long 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 salt used for @h_donor_tax_id. 101 */ 102 const char *donor_tax_id; 103 104 /** 105 * The cleartext tax id of the user 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 const 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 { 196 struct DONAU_BlindedDonationUnitSignature *blinded_sigs = 197 biresp->details.ok.blinded_sigs; 198 if (biresp->details.ok.num_blinded_sigs != ss->num_bkp) 199 { 200 GNUNET_break (0); 201 TALER_TESTING_interpreter_fail (ss->is); 202 return; 203 } 204 for (size_t i = 0; i < ss->num_bkp; i++) 205 { 206 struct DONAU_UniqueDonorIdentifierHashP checkudi_hash; 207 208 GNUNET_assert (GNUNET_OK == 209 DONAU_donation_unit_sig_unblind ( 210 &ss->receipts[i].donation_unit_sig, 211 &blinded_sigs[i], 212 &ss->blinding_secrets[i], 213 &ss->h_udis[i], 214 ss->alg_values[i], 215 &ss->selected_pks[i])); 216 217 /* check udi message */ 218 DONAU_unique_donor_id_hash ( 219 &ss->h_donor_tax_id, 220 &ss->receipts[i].nonce, 221 &checkudi_hash); 222 GNUNET_assert (0 == GNUNET_CRYPTO_hash_cmp (&checkudi_hash.hash, 223 &ss->h_udis[i].hash)); 224 /* check signature */ 225 if (GNUNET_OK != 226 DONAU_donation_receipt_verify ( 227 &ss->selected_pks[i], 228 &checkudi_hash, 229 &ss->receipts[i].donation_unit_sig)) 230 { 231 GNUNET_break_op (0); 232 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 233 "Donation receipt signature invalid!\n"); 234 TALER_TESTING_interpreter_fail (ss->is); 235 return; 236 } 237 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 238 "Donation receipt signature valid!\n"); 239 } 240 } 241 TALER_TESTING_interpreter_next (ss->is); 242 } 243 244 245 /** 246 * Runs phase two, the actual issue receipts operation. 247 * Started once the preparation for CS-donation-units is 248 * done. 249 * @param cls closure. 250 */ 251 static void 252 phase_two (void *cls) 253 { 254 struct StatusState *ss = cls; 255 const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = ss->bkps; 256 257 ss->birh = DONAU_charity_issue_receipt ( 258 TALER_TESTING_interpreter_get_context (ss->is), 259 TALER_TESTING_get_donau_url (ss->is), 260 &ss->charity_priv, 261 ss->charity_id, 262 ss->year, 263 ss->num_bkp, 264 bkps, 265 &issue_receipts_status_cb, 266 ss); 267 } 268 269 270 /** 271 * Function called when stage 1 of CS issue is finished (request r_pub's) 272 * 273 * @param csr_data the `struct CSR_Data *` 274 * @param csrresp replies from the /csr-issue request 275 */ 276 static void 277 cs_stage_two_callback ( 278 struct CSR_Data *csr_data, 279 const struct DONAU_CsRBatchIssueResponse *csrresp) 280 { 281 struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi = 282 &csr_data->ss->bkps[csr_data->position].blinded_udi; 283 284 if (csrresp->hr.http_status != MHD_HTTP_CREATED) 285 { 286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 287 "Unexpected HTTP response code: %d in %s:%u\n", 288 csrresp->hr.http_status, 289 __FILE__, 290 __LINE__); 291 json_dumpf (csrresp->hr.reply, 292 stderr, 293 0); 294 TALER_TESTING_interpreter_fail (csr_data->ss->is); 295 return; 296 } 297 298 { 299 struct DONAU_DonationUnitPublicKey *cs_pk = 300 &csr_data->ss->keys->donation_unit_keys[csr_data->position].key; 301 struct DONAU_BatchIssueValues *alg_values = GNUNET_new (struct 302 DONAU_BatchIssueValues); 303 struct DONAU_BudiMasterSecretP ps; 304 struct DONAU_UniqueDonorIdentifierHashP *udi_hash = 305 &csr_data->ss->h_udis[csr_data->position]; 306 union GNUNET_CRYPTO_BlindingSecretP *blinding_secret = 307 &csr_data->ss->blinding_secrets[csr_data->position]; 308 struct DONAU_UniqueDonorIdentifierNonce *udi_nonce = 309 &csr_data->ss->receipts[csr_data->position].nonce; 310 311 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == cs_pk->bsign_pub_key->cipher); 312 313 DONAU_donation_unit_ewv_copy (alg_values, 314 &csrresp->details.ok. 315 alg_values); 316 GNUNET_CRYPTO_random_block (&ps, 317 sizeof (ps)); 318 DONAU_budi_secret_create (&ps, 319 alg_values, 320 blinding_secret); 321 GNUNET_assert (GNUNET_OK == 322 DONAU_donation_unit_blind ( 323 cs_pk, 324 blinding_secret, 325 &csr_data->nonce, /* nonce only needed for cs */ 326 udi_nonce, 327 &csr_data->ss->h_donor_tax_id, 328 alg_values, 329 udi_hash, 330 blinded_udi)); 331 csr_data->ss->alg_values[csr_data->position] = alg_values; 332 csr_data->ss->cs_pending--; 333 } 334 if (0 == csr_data->ss->cs_pending) 335 phase_two (csr_data->ss); 336 } 337 338 339 /** 340 * Run the command. 341 * 342 * @param cls closure. 343 * @param cmd the command being executed. 344 * @param is the interpreter state. 345 */ 346 static void 347 status_run (void *cls, 348 const struct TALER_TESTING_Command *cmd, 349 struct TALER_TESTING_Interpreter *is) 350 { 351 struct StatusState *ss = cls; 352 353 (void) cmd; 354 ss->is = is; 355 356 /* Get charity id and the charity private key from trait */ 357 { 358 const struct TALER_TESTING_Command *charity_post_cmd; 359 const uint64_t *charity_id; 360 const struct DONAU_CharityPrivateKeyP *charity_priv; 361 362 363 charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is, 364 ss-> 365 charity_reference); 366 367 if ( (GNUNET_OK != 368 TALER_TESTING_get_trait_charity_id (charity_post_cmd, 369 &charity_id) ) || 370 (GNUNET_OK != 371 TALER_TESTING_get_trait_charity_priv (charity_post_cmd, 372 &charity_priv)) ) 373 { 374 GNUNET_break (0); 375 TALER_TESTING_interpreter_fail (is); 376 return; 377 } 378 ss->charity_id = (uint64_t) *(charity_id); 379 ss->charity_priv = *(charity_priv); 380 } 381 382 /* Get donau keys from trait */ 383 { 384 const struct TALER_TESTING_Command *keys_cmd; 385 struct DONAU_Keys *keys; 386 387 keys_cmd = TALER_TESTING_interpreter_lookup_command (is, 388 "get-donau"); 389 390 if (GNUNET_OK != 391 TALER_TESTING_get_trait_donau_keys (keys_cmd, &keys)) 392 { 393 GNUNET_break (0); 394 TALER_TESTING_interpreter_fail (is); 395 return; 396 } 397 ss->keys = keys; 398 } 399 400 /* Get the donation_unit_keys for requested amount */ 401 { 402 enum GNUNET_GenericReturnValue sret; 403 404 sret = DONAU_select_donation_unit_keys_for_amount ( 405 ss->keys, 406 &ss->amount, 407 ss->year, 408 &ss->selected_pks, 409 &ss->num_bkp); 410 411 if (GNUNET_SYSERR == sret) 412 { 413 GNUNET_break (0); 414 TALER_TESTING_interpreter_fail (is); 415 return; 416 } 417 if ((GNUNET_NO == sret) || (0 == ss->num_bkp)) 418 { 419 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 420 "Could not compose exact amount %s from donation units for %u\n", 421 TALER_amount2s (&ss->amount), 422 (unsigned int) ss->year); 423 TALER_TESTING_interpreter_fail (is); 424 return; 425 } 426 } 427 428 ss->bkps = 429 GNUNET_new_array (ss->num_bkp, 430 struct DONAU_BlindedUniqueDonorIdentifierKeyPair); 431 ss->blinding_secrets = 432 GNUNET_new_array (ss->num_bkp, 433 union GNUNET_CRYPTO_BlindingSecretP); 434 ss->receipts = 435 GNUNET_new_array (ss->num_bkp, 436 struct DONAU_DonationReceipt); 437 ss->alg_values = 438 GNUNET_new_array (ss->num_bkp, 439 const struct DONAU_BatchIssueValues *); 440 ss->h_udis = 441 GNUNET_new_array (ss->num_bkp, 442 struct DONAU_UniqueDonorIdentifierHashP); 443 for (size_t cnt = 0; cnt < ss->num_bkp; cnt++) 444 { 445 struct DONAU_UniqueDonorIdentifierNonce *udi_nonce 446 = &ss->receipts[cnt].nonce; 447 struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi 448 = &ss->bkps[cnt].blinded_udi; 449 struct DONAU_UniqueDonorIdentifierHashP *udi_hash 450 = &ss->h_udis[cnt]; 451 struct DONAU_BudiMasterSecretP ps; 452 const struct DONAU_BatchIssueValues *alg_values; 453 454 DONAU_donation_unit_pub_hash (&ss->selected_pks[cnt], 455 &ss->bkps[cnt].h_donation_unit_pub); 456 ss->receipts[cnt].h_donation_unit_pub = ss->bkps[cnt].h_donation_unit_pub; 457 GNUNET_CRYPTO_random_block (&ps, 458 sizeof (ps)); 459 GNUNET_CRYPTO_random_block (udi_nonce, 460 sizeof (*udi_nonce)); 461 switch (ss->selected_pks[cnt].bsign_pub_key->cipher) 462 { 463 case GNUNET_CRYPTO_BSA_RSA: 464 alg_values = DONAU_donation_unit_ewv_rsa_singleton (); 465 DONAU_budi_secret_create (&ps, 466 alg_values, 467 &ss->blinding_secrets[cnt]); 468 GNUNET_assert (GNUNET_OK == 469 DONAU_donation_unit_blind ( 470 &ss->selected_pks[cnt], 471 &ss->blinding_secrets[cnt], 472 NULL, /* no cs-nonce needed for rsa */ 473 udi_nonce, 474 &ss->h_donor_tax_id, 475 alg_values, 476 udi_hash, 477 blinded_udi)); 478 ss->alg_values[cnt] = alg_values; 479 break; 480 case GNUNET_CRYPTO_BSA_CS: 481 { 482 struct CSR_Data *csr_data = GNUNET_new (struct CSR_Data); 483 484 TALER_cs_withdraw_nonce_derive ( // FIXME: write new method 485 (struct TALER_PlanchetMasterSecretP *) &ps, 486 &csr_data->nonce.cs_nonce); 487 csr_data->ss = ss; 488 csr_data->position = cnt; 489 csr_data->csr_handle = DONAU_csr_issue ( 490 TALER_TESTING_interpreter_get_context (is), 491 TALER_TESTING_get_donau_url (is), 492 &ss->selected_pks[cnt], 493 &csr_data->nonce.cs_nonce, 494 &cs_stage_two_callback, 495 csr_data); 496 if (NULL == csr_data->csr_handle) 497 { 498 GNUNET_break (0); 499 } 500 ss->cs_pending++; 501 break; 502 } 503 default: 504 GNUNET_break (0); 505 } 506 } 507 if (0 == ss->cs_pending) 508 phase_two (ss); 509 } 510 511 512 /** 513 * Cleanup the state from a "issue receipt status" CMD, and possibly 514 * cancel a pending operation thereof. 515 * 516 * @param cls closure. 517 * @param cmd the command which is being cleaned up. 518 */ 519 static void 520 cleanup (void *cls, 521 const struct TALER_TESTING_Command *cmd) 522 { 523 struct StatusState *ss = cls; 524 525 if (NULL != ss->birh) 526 { 527 // log incomplete command 528 TALER_TESTING_command_incomplete (ss->is, 529 cmd->label); 530 DONAU_charity_issue_receipt_cancel (ss->birh); 531 ss->birh = NULL; 532 } 533 534 GNUNET_free (ss->selected_pks); 535 GNUNET_free (ss->h_udis); 536 GNUNET_free (ss->alg_values); 537 GNUNET_free (ss->blinding_secrets); 538 if (NULL != ss->receipts) 539 { 540 for (unsigned int i = 0; i < ss->num_bkp; i++) 541 GNUNET_CRYPTO_unblinded_sig_decref ( 542 ss->receipts[i].donation_unit_sig.unblinded_sig); 543 GNUNET_free (ss->receipts); 544 } 545 GNUNET_free (ss->bkps); 546 GNUNET_free (ss); 547 } 548 549 550 /** 551 * Offer internal data from a "deposit" CMD, to other commands. 552 * 553 * @param cls closure. 554 * @param[out] ret result. 555 * @param trait name of the trait. 556 * @param index index number of the object to offer. 557 * @return #GNUNET_OK on success. 558 */ 559 static enum GNUNET_GenericReturnValue 560 issue_receipts_traits (void *cls, 561 const void **ret, 562 const char *trait, 563 unsigned int index) 564 { 565 struct StatusState *ss = cls; 566 struct TALER_TESTING_Trait traits[] = { 567 TALER_TESTING_make_trait_donor_salt (ss->donor_salt), 568 TALER_TESTING_make_trait_donor_tax_id (ss->donor_tax_id), 569 TALER_TESTING_make_trait_salted_tax_id_hash ( 570 (const struct DONAU_HashDonorTaxId *) &ss->h_donor_tax_id), 571 TALER_TESTING_make_trait_donation_receipts ( 572 (const struct DONAU_DonationReceipt **) &ss->receipts), 573 TALER_TESTING_make_trait_number_receipts (&ss->num_bkp), 574 TALER_TESTING_trait_end () 575 }; 576 577 return TALER_TESTING_get_trait (traits, 578 ret, 579 trait, 580 index); 581 } 582 583 584 struct TALER_TESTING_Command 585 TALER_TESTING_cmd_issue_receipts (const char *label, 586 const char *charity_reference, 587 const bool uses_cs, 588 const uint64_t year, 589 const char *donor_tax_id, 590 const char *salt, 591 const char *issue_amount, 592 unsigned int expected_response_code) 593 { 594 struct StatusState *ss; 595 596 ss = GNUNET_new (struct StatusState); 597 598 ss->year = year; 599 ss->charity_reference = charity_reference; 600 ss->expected_response_code = expected_response_code; 601 ss->uses_cs = uses_cs; 602 ss->donor_salt = (const char*) salt; 603 ss->donor_tax_id = (const char*) donor_tax_id; 604 if (GNUNET_OK != 605 TALER_string_to_amount (issue_amount, 606 &ss->amount)) 607 { 608 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 609 "Failed to parse amount `%s' at %s\n", 610 issue_amount, 611 label); 612 GNUNET_assert (0); 613 } 614 615 if (! DONAU_compute_salted_tax_id_hash (donor_tax_id, 616 salt, 617 ss->h_donor_tax_id.hash)) 618 { 619 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 620 "Hash was not received"); 621 GNUNET_assert (0); 622 } 623 624 { 625 struct TALER_TESTING_Command cmd = { 626 .cls = ss, 627 .label = label, 628 .run = &status_run, 629 .cleanup = &cleanup, 630 .traits = &issue_receipts_traits 631 }; 632 633 return cmd; 634 635 } 636 }