testing_api_cmd_issue_receipts.c (17295B)
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 (&ps, 310 sizeof (ps)); 311 DONAU_budi_secret_create (&ps, 312 alg_values, 313 blinding_secret); 314 GNUNET_assert (GNUNET_OK == 315 DONAU_donation_unit_blind ( 316 cs_pk, 317 blinding_secret, 318 &csr_data->nonce, /* nonce only needed for cs */ 319 udi_nonce, 320 &csr_data->ss->h_donor_tax_id, 321 alg_values, 322 udi_hash, 323 blinded_udi)); 324 csr_data->ss->alg_values[csr_data->position] = alg_values; 325 csr_data->ss->cs_pending--; 326 } 327 if (0 == csr_data->ss->cs_pending) 328 phase_two (csr_data->ss); 329 } 330 331 332 /** 333 * Run the command. 334 * 335 * @param cls closure. 336 * @param cmd the command being executed. 337 * @param is the interpreter state. 338 */ 339 static void 340 status_run (void *cls, 341 const struct TALER_TESTING_Command *cmd, 342 struct TALER_TESTING_Interpreter *is) 343 { 344 struct StatusState *ss = cls; 345 346 (void) cmd; 347 ss->is = is; 348 349 /* Get charity id and the charity private key from trait */ 350 { 351 const struct TALER_TESTING_Command *charity_post_cmd; 352 const uint64_t *charity_id; 353 const struct DONAU_CharityPrivateKeyP *charity_priv; 354 355 356 charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is, 357 ss-> 358 charity_reference); 359 360 if ( (GNUNET_OK != 361 TALER_TESTING_get_trait_charity_id (charity_post_cmd, 362 &charity_id) ) || 363 (GNUNET_OK != 364 TALER_TESTING_get_trait_charity_priv (charity_post_cmd, 365 &charity_priv)) ) 366 { 367 GNUNET_break (0); 368 TALER_TESTING_interpreter_fail (is); 369 return; 370 } 371 ss->charity_id = (uint64_t) *(charity_id); 372 ss->charity_priv = *(charity_priv); 373 } 374 375 /* Get donau keys from trait */ 376 { 377 const struct TALER_TESTING_Command *keys_cmd; 378 struct DONAU_Keys *keys; 379 380 keys_cmd = TALER_TESTING_interpreter_lookup_command (is, 381 "get-donau"); 382 383 if (GNUNET_OK != 384 TALER_TESTING_get_trait_donau_keys (keys_cmd, &keys)) 385 { 386 GNUNET_break (0); 387 TALER_TESTING_interpreter_fail (is); 388 return; 389 } 390 ss->keys = keys; 391 } 392 393 /* Get the donation_unit_keys for requested amount */ 394 { 395 enum GNUNET_GenericReturnValue sret; 396 397 sret = DONAU_select_donation_unit_keys_for_amount ( 398 ss->keys, 399 &ss->amount, 400 ss->year, 401 &ss->selected_pks, 402 &ss->num_bkp); 403 404 if (GNUNET_SYSERR == sret) 405 { 406 GNUNET_break (0); 407 TALER_TESTING_interpreter_fail (is); 408 return; 409 } 410 if ((GNUNET_NO == sret) || (0 == ss->num_bkp)) 411 { 412 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 413 "Could not compose exact amount %s from donation units for %u\n", 414 TALER_amount2s (&ss->amount), 415 (unsigned int) ss->year); 416 TALER_TESTING_interpreter_fail (is); 417 return; 418 } 419 } 420 421 ss->bkps = 422 GNUNET_new_array (ss->num_bkp, struct 423 DONAU_BlindedUniqueDonorIdentifierKeyPair); 424 ss->blinding_secrets = 425 GNUNET_new_array (ss->num_bkp, union GNUNET_CRYPTO_BlindingSecretP); 426 ss->receipts = 427 GNUNET_new_array (ss->num_bkp, struct DONAU_DonationReceipt); 428 ss->alg_values = 429 GNUNET_new_array (ss->num_bkp, const struct DONAU_BatchIssueValues *); 430 ss->h_udis = 431 GNUNET_new_array (ss->num_bkp, struct DONAU_UniqueDonorIdentifierHashP); 432 for (size_t cnt = 0; cnt < ss->num_bkp; cnt++) 433 { 434 struct DONAU_UniqueDonorIdentifierNonce *udi_nonce 435 = &ss->receipts[cnt].nonce; 436 struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi 437 = &ss->bkps[cnt].blinded_udi; 438 struct DONAU_UniqueDonorIdentifierHashP *udi_hash 439 = &ss->h_udis[cnt]; 440 struct DONAU_BudiMasterSecretP ps; 441 const struct DONAU_BatchIssueValues *alg_values; 442 443 DONAU_donation_unit_pub_hash (&ss->selected_pks[cnt], 444 &ss->bkps[cnt].h_donation_unit_pub); 445 ss->receipts[cnt].h_donation_unit_pub = ss->bkps[cnt].h_donation_unit_pub; 446 GNUNET_CRYPTO_random_block (&ps, 447 sizeof (ps)); 448 GNUNET_CRYPTO_random_block (udi_nonce, 449 sizeof (*udi_nonce)); 450 switch (ss->selected_pks[cnt].bsign_pub_key->cipher) 451 { 452 case GNUNET_CRYPTO_BSA_RSA: 453 alg_values = DONAU_donation_unit_ewv_rsa_singleton (); 454 DONAU_budi_secret_create (&ps, 455 alg_values, 456 &ss->blinding_secrets[cnt]); 457 GNUNET_assert (GNUNET_OK == 458 DONAU_donation_unit_blind ( 459 &ss->selected_pks[cnt], 460 &ss->blinding_secrets[cnt], 461 NULL, /* no cs-nonce needed for rsa */ 462 udi_nonce, 463 &ss->h_donor_tax_id, 464 alg_values, 465 udi_hash, 466 blinded_udi)); 467 ss->alg_values[cnt] = alg_values; 468 break; 469 case GNUNET_CRYPTO_BSA_CS: 470 { 471 struct CSR_Data *csr_data = GNUNET_new (struct CSR_Data); 472 473 TALER_cs_withdraw_nonce_derive ( // FIXME: write new method 474 (struct TALER_PlanchetMasterSecretP *) &ps, 475 &csr_data->nonce.cs_nonce); 476 csr_data->ss = ss; 477 csr_data->position = cnt; 478 csr_data->csr_handle = DONAU_csr_issue ( 479 TALER_TESTING_interpreter_get_context (is), 480 TALER_TESTING_get_donau_url (is), 481 &ss->selected_pks[cnt], 482 &csr_data->nonce.cs_nonce, 483 &cs_stage_two_callback, 484 csr_data); 485 if (NULL == csr_data->csr_handle) 486 { 487 GNUNET_break (0); 488 } 489 ss->cs_pending++; 490 break; 491 } 492 default: 493 GNUNET_break (0); 494 } 495 } 496 if (0 == ss->cs_pending) 497 phase_two (ss); 498 } 499 500 501 /** 502 * Cleanup the state from a "issue receipt status" CMD, and possibly 503 * cancel a pending operation thereof. 504 * 505 * @param cls closure. 506 * @param cmd the command which is being cleaned up. 507 */ 508 static void 509 cleanup (void *cls, 510 const struct TALER_TESTING_Command *cmd) 511 { 512 struct StatusState *ss = cls; 513 514 if (NULL != ss->birh) 515 { 516 // log incomplete command 517 TALER_TESTING_command_incomplete (ss->is, 518 cmd->label); 519 DONAU_charity_issue_receipt_cancel (ss->birh); 520 ss->birh = NULL; 521 } 522 523 if (ss->selected_pks) 524 GNUNET_array_grow (ss->selected_pks, ss->num_bkp, 0); 525 526 GNUNET_free (ss->h_udis); 527 GNUNET_free (ss->alg_values); 528 GNUNET_free (ss->blinding_secrets); 529 GNUNET_free (ss->bkps); 530 GNUNET_free (ss); 531 } 532 533 534 /** 535 * Offer internal data from a "deposit" CMD, to other commands. 536 * 537 * @param cls closure. 538 * @param[out] ret result. 539 * @param trait name of the trait. 540 * @param index index number of the object to offer. 541 * @return #GNUNET_OK on success. 542 */ 543 static enum GNUNET_GenericReturnValue 544 issue_receipts_traits (void *cls, 545 const void **ret, 546 const char *trait, 547 unsigned int index) 548 { 549 struct StatusState *ss = cls; 550 struct TALER_TESTING_Trait traits[] = { 551 TALER_TESTING_make_trait_donor_salt (ss->donor_salt), 552 TALER_TESTING_make_trait_donor_tax_id (ss->donor_tax_id), 553 TALER_TESTING_make_trait_salted_tax_id_hash ( 554 (const struct DONAU_HashDonorTaxId *) &ss->h_donor_tax_id), 555 TALER_TESTING_make_trait_donation_receipts ( 556 (const struct DONAU_DonationReceipt **) &ss->receipts), 557 TALER_TESTING_make_trait_number_receipts ((const size_t *) &ss->num_bkp), 558 TALER_TESTING_trait_end () 559 }; 560 561 return TALER_TESTING_get_trait (traits, 562 ret, 563 trait, 564 index); 565 } 566 567 568 struct TALER_TESTING_Command 569 TALER_TESTING_cmd_issue_receipts (const char *label, 570 const char *charity_reference, 571 const bool uses_cs, 572 const uint64_t year, 573 const char *donor_tax_id, 574 const char *salt, 575 const char *issue_amount, 576 unsigned int expected_response_code) 577 { 578 struct StatusState *ss; 579 580 ss = GNUNET_new (struct StatusState); 581 582 ss->year = year; 583 ss->charity_reference = charity_reference; 584 ss->expected_response_code = expected_response_code; 585 ss->uses_cs = uses_cs; 586 ss->donor_salt = (const char*) salt; 587 ss->donor_tax_id = (const char*) donor_tax_id; 588 if (GNUNET_OK != 589 TALER_string_to_amount (issue_amount, 590 &ss->amount)) 591 { 592 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 593 "Failed to parse amount `%s' at %s\n", 594 issue_amount, 595 label); 596 GNUNET_assert (0); 597 } 598 599 if (! DONAU_compute_salted_tax_id_hash (donor_tax_id, 600 salt, 601 ss->h_donor_tax_id.hash)) 602 { 603 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 604 "Hash was not received"); 605 GNUNET_assert (0); 606 } 607 608 { 609 struct TALER_TESTING_Command cmd = { 610 .cls = ss, 611 .label = label, 612 .run = &status_run, 613 .cleanup = &cleanup, 614 .traits = &issue_receipts_traits 615 }; 616 617 return cmd; 618 619 } 620 }