donau

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

testing_api_cmd_issue_receipts.c (17295B)


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