donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

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 }