donau

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

testing_api_cmd_issue_receipts.c (18693B)


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