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