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