donau

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

testing_api_cmd_issue_receipts.c (17480B)


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