donau

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

testing_api_cmd_issue_receipts.c (17379B)


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