donau

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

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 }