donau

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

testing_api_cmd_issue_receipts.c (19155B)


      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   if (MHD_HTTP_OK != biresp->hr.http_status)
    196   {
    197     TALER_TESTING_interpreter_next (ss->is);
    198     return;
    199   }
    200   {
    201     struct DONAU_BlindedDonationUnitSignature *blinded_sigs =
    202       biresp->details.ok.blinded_sigs;
    203     if (biresp->details.ok.num_blinded_sigs != ss->num_bkp)
    204     {
    205       GNUNET_break (0);
    206       TALER_TESTING_interpreter_fail (ss->is);
    207       return;
    208     }
    209     for (size_t i = 0; i < ss->num_bkp; i++)
    210     {
    211       struct DONAU_UniqueDonorIdentifierHashP checkudi_hash;
    212 
    213       GNUNET_assert (GNUNET_OK ==
    214                      DONAU_donation_unit_sig_unblind (
    215                        &ss->receipts[i].donation_unit_sig,
    216                        &blinded_sigs[i],
    217                        &ss->blinding_secrets[i],
    218                        &ss->h_udis[i],
    219                        ss->alg_values[i],
    220                        &ss->selected_pks[i]));
    221 
    222       /* check udi message */
    223       DONAU_unique_donor_id_hash (
    224         &ss->h_donor_tax_id,
    225         &ss->receipts[i].nonce,
    226         &checkudi_hash);
    227       GNUNET_assert (0 == GNUNET_CRYPTO_hash_cmp (&checkudi_hash.hash,
    228                                                   &ss->h_udis[i].hash));
    229       /* check signature */
    230       if (GNUNET_OK !=
    231           DONAU_donation_receipt_verify (
    232             &ss->selected_pks[i],
    233             &checkudi_hash,
    234             &ss->receipts[i].donation_unit_sig))
    235       {
    236         GNUNET_break_op (0);
    237         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    238                     "Donation receipt signature invalid!\n");
    239         TALER_TESTING_interpreter_fail (ss->is);
    240         return;
    241       }
    242       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    243                   "Donation receipt signature valid!\n");
    244     }
    245   }
    246   TALER_TESTING_interpreter_next (ss->is);
    247 }
    248 
    249 
    250 /**
    251  * Runs phase two, the actual issue receipts operation.
    252  * Started once the preparation for CS-donation-units is
    253  * done.
    254  * @param cls closure.
    255  */
    256 static void
    257 phase_two (void *cls)
    258 {
    259   struct StatusState *ss = cls;
    260   const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = ss->bkps;
    261 
    262   ss->birh = DONAU_charity_issue_receipt (
    263     TALER_TESTING_interpreter_get_context (ss->is),
    264     TALER_TESTING_get_donau_url (ss->is),
    265     &ss->charity_priv,
    266     ss->charity_id,
    267     ss->year,
    268     ss->num_bkp,
    269     bkps,
    270     &issue_receipts_status_cb,
    271     ss);
    272 }
    273 
    274 
    275 /**
    276  * Function called when stage 1 of CS issue is finished (request r_pub's)
    277  *
    278  * @param csr_data the `struct CSR_Data *`
    279  * @param csrresp replies from the /csr-issue request
    280  */
    281 static void
    282 cs_stage_two_callback (
    283   struct CSR_Data *csr_data,
    284   const struct DONAU_CsRBatchIssueResponse *csrresp)
    285 {
    286   struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi =
    287     &csr_data->ss->bkps[csr_data->position].blinded_udi;
    288 
    289   if (csrresp->hr.http_status != MHD_HTTP_CREATED)
    290   {
    291     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    292                 "Unexpected HTTP response code: %d in %s:%u\n",
    293                 csrresp->hr.http_status,
    294                 __FILE__,
    295                 __LINE__);
    296     json_dumpf (csrresp->hr.reply,
    297                 stderr,
    298                 0);
    299     TALER_TESTING_interpreter_fail (csr_data->ss->is);
    300     GNUNET_free (csr_data);
    301     return;
    302   }
    303 
    304   {
    305     struct DONAU_DonationUnitPublicKey *cs_pk =
    306       &csr_data->ss->selected_pks[csr_data->position];
    307     struct DONAU_BatchIssueValues *alg_values
    308       = GNUNET_new (struct DONAU_BatchIssueValues);
    309     struct DONAU_BudiMasterSecretP ps;
    310     struct DONAU_UniqueDonorIdentifierHashP *udi_hash =
    311       &csr_data->ss->h_udis[csr_data->position];
    312     union GNUNET_CRYPTO_BlindingSecretP *blinding_secret =
    313       &csr_data->ss->blinding_secrets[csr_data->position];
    314     struct DONAU_UniqueDonorIdentifierNonce *udi_nonce =
    315       &csr_data->ss->receipts[csr_data->position].nonce;
    316 
    317     GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
    318                    cs_pk->bsign_pub_key->cipher);
    319 
    320     DONAU_donation_unit_ewv_copy (
    321       alg_values,
    322       &csrresp->details.ok.alg_values);
    323     GNUNET_CRYPTO_random_block (&ps,
    324                                 sizeof (ps));
    325     DONAU_budi_secret_create (&ps,
    326                               alg_values,
    327                               blinding_secret);
    328     GNUNET_assert (GNUNET_OK ==
    329                    DONAU_donation_unit_blind (
    330                      cs_pk,
    331                      blinding_secret,
    332                      &csr_data->nonce,        /* nonce only needed for cs */
    333                      udi_nonce,
    334                      &csr_data->ss->h_donor_tax_id,
    335                      alg_values,
    336                      udi_hash,
    337                      blinded_udi));
    338     csr_data->ss->alg_values[csr_data->position] = alg_values;
    339     csr_data->ss->cs_pending--;
    340   }
    341   {
    342     struct StatusState *ss = csr_data->ss;
    343 
    344     GNUNET_free (csr_data);
    345     if (0 == ss->cs_pending)
    346       phase_two (ss);
    347   }
    348 }
    349 
    350 
    351 /**
    352  * Run the command.
    353  *
    354  * @param cls closure.
    355  * @param cmd the command being executed.
    356  * @param is the interpreter state.
    357  */
    358 static void
    359 status_run (void *cls,
    360             const struct TALER_TESTING_Command *cmd,
    361             struct TALER_TESTING_Interpreter *is)
    362 {
    363   struct StatusState *ss = cls;
    364 
    365   (void) cmd;
    366   ss->is = is;
    367 
    368   /* Get charity id and the charity private key from trait */
    369   {
    370     const struct TALER_TESTING_Command *charity_post_cmd;
    371     const uint64_t *charity_id;
    372     const struct DONAU_CharityPrivateKeyP *charity_priv;
    373 
    374 
    375     charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is,
    376                                                                  ss->
    377                                                                  charity_reference);
    378 
    379     if ( (GNUNET_OK !=
    380           TALER_TESTING_get_trait_charity_id (charity_post_cmd,
    381                                               &charity_id) ) ||
    382          (GNUNET_OK !=
    383           TALER_TESTING_get_trait_charity_priv (charity_post_cmd,
    384                                                 &charity_priv)) )
    385     {
    386       GNUNET_break (0);
    387       TALER_TESTING_interpreter_fail (is);
    388       return;
    389     }
    390     ss->charity_id = (uint64_t) *(charity_id);
    391     ss->charity_priv = *(charity_priv);
    392   }
    393 
    394   /* Get donau keys from trait */
    395   {
    396     const struct TALER_TESTING_Command *keys_cmd;
    397     struct DONAU_Keys *keys;
    398 
    399     keys_cmd = TALER_TESTING_interpreter_lookup_command (is,
    400                                                          "get-donau");
    401 
    402     if (GNUNET_OK !=
    403         TALER_TESTING_get_trait_donau_keys (keys_cmd, &keys))
    404     {
    405       GNUNET_break (0);
    406       TALER_TESTING_interpreter_fail (is);
    407       return;
    408     }
    409     ss->keys = keys;
    410   }
    411 
    412   /* Get the donation_unit_keys for requested amount */
    413   {
    414     enum GNUNET_GenericReturnValue sret;
    415 
    416     sret = DONAU_select_donation_unit_keys_for_amount (
    417       ss->keys,
    418       &ss->amount,
    419       ss->year,
    420       &ss->selected_pks,
    421       &ss->num_bkp);
    422 
    423     if (GNUNET_SYSERR == sret)
    424     {
    425       GNUNET_break (0);
    426       TALER_TESTING_interpreter_fail (is);
    427       return;
    428     }
    429     if ((GNUNET_NO == sret) || (0 == ss->num_bkp))
    430     {
    431       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    432                   "Could not compose exact amount %s from donation units for %u\n",
    433                   TALER_amount2s (&ss->amount),
    434                   (unsigned int) ss->year);
    435       TALER_TESTING_interpreter_fail (is);
    436       return;
    437     }
    438   }
    439 
    440   ss->bkps =
    441     GNUNET_new_array (ss->num_bkp,
    442                       struct DONAU_BlindedUniqueDonorIdentifierKeyPair);
    443   ss->blinding_secrets =
    444     GNUNET_new_array (ss->num_bkp,
    445                       union GNUNET_CRYPTO_BlindingSecretP);
    446   ss->receipts =
    447     GNUNET_new_array (ss->num_bkp,
    448                       struct DONAU_DonationReceipt);
    449   ss->alg_values =
    450     GNUNET_new_array (ss->num_bkp,
    451                       struct DONAU_BatchIssueValues *);
    452   ss->h_udis =
    453     GNUNET_new_array (ss->num_bkp,
    454                       struct DONAU_UniqueDonorIdentifierHashP);
    455   for (size_t cnt = 0; cnt < ss->num_bkp; cnt++)
    456   {
    457     struct DONAU_UniqueDonorIdentifierNonce *udi_nonce
    458       = &ss->receipts[cnt].nonce;
    459     struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi
    460       = &ss->bkps[cnt].blinded_udi;
    461     struct DONAU_UniqueDonorIdentifierHashP *udi_hash
    462       = &ss->h_udis[cnt];
    463     struct DONAU_BudiMasterSecretP ps;
    464     struct DONAU_BatchIssueValues *alg_values;
    465 
    466     DONAU_donation_unit_pub_hash (&ss->selected_pks[cnt],
    467                                   &ss->bkps[cnt].h_donation_unit_pub);
    468     ss->receipts[cnt].h_donation_unit_pub = ss->bkps[cnt].h_donation_unit_pub;
    469     GNUNET_CRYPTO_random_block (&ps,
    470                                 sizeof (ps));
    471     GNUNET_CRYPTO_random_block (udi_nonce,
    472                                 sizeof (*udi_nonce));
    473     switch (ss->selected_pks[cnt].bsign_pub_key->cipher)
    474     {
    475     case GNUNET_CRYPTO_BSA_RSA:
    476       alg_values = DONAU_donation_unit_ewv_rsa_singleton ();
    477       DONAU_budi_secret_create (&ps,
    478                                 alg_values,
    479                                 &ss->blinding_secrets[cnt]);
    480       GNUNET_assert (GNUNET_OK ==
    481                      DONAU_donation_unit_blind (
    482                        &ss->selected_pks[cnt],
    483                        &ss->blinding_secrets[cnt],
    484                        NULL,                    /* no cs-nonce needed for rsa */
    485                        udi_nonce,
    486                        &ss->h_donor_tax_id,
    487                        alg_values,
    488                        udi_hash,
    489                        blinded_udi));
    490       ss->alg_values[cnt] = alg_values;
    491       break;
    492     case GNUNET_CRYPTO_BSA_CS:
    493       {
    494         struct CSR_Data *csr_data = GNUNET_new (struct CSR_Data);
    495 
    496         // Trying to make it compile after 1e79313 on exchange
    497         // TALER_cs_withdraw_nonce_derive ( // FIXME: write new method
    498         //  (struct TALER_PlanchetMasterSecretP *) &ps,
    499         //  &csr_data->nonce.cs_nonce);
    500         DONAU_cs_issue_nonce_derive (&ps, // FIXME: I assume same fixme as before...
    501                                      &csr_data->nonce.cs_nonce);
    502         csr_data->ss = ss;
    503         csr_data->position = cnt;
    504         csr_data->csr_handle = DONAU_csr_issue (
    505           TALER_TESTING_interpreter_get_context (is),
    506           TALER_TESTING_get_donau_url (is),
    507           &ss->selected_pks[cnt],
    508           &csr_data->nonce.cs_nonce,
    509           &cs_stage_two_callback,
    510           csr_data);
    511         if (NULL == csr_data->csr_handle)
    512         {
    513           GNUNET_break (0);
    514         }
    515         ss->cs_pending++;
    516         break;
    517       }
    518     default:
    519       GNUNET_break (0);
    520     }
    521   }
    522   if (0 == ss->cs_pending)
    523     phase_two (ss);
    524 }
    525 
    526 
    527 /**
    528  * Cleanup the state from a "issue receipt status" CMD, and possibly
    529  * cancel a pending operation thereof.
    530  *
    531  * @param cls closure.
    532  * @param cmd the command which is being cleaned up.
    533  */
    534 static void
    535 cleanup (void *cls,
    536          const struct TALER_TESTING_Command *cmd)
    537 {
    538   struct StatusState *ss = cls;
    539 
    540   if (NULL != ss->birh)
    541   {
    542     // log incomplete command
    543     TALER_TESTING_command_incomplete (ss->is,
    544                                       cmd->label);
    545     DONAU_charity_issue_receipt_cancel (ss->birh);
    546     ss->birh = NULL;
    547   }
    548 
    549   GNUNET_free (ss->selected_pks);
    550   GNUNET_free (ss->h_udis);
    551   if (NULL != ss->alg_values)
    552   {
    553     /* For CS units, cs_stage_two_callback allocated a per-unit
    554        DONAU_BatchIssueValues with an incref'd blinding_inputs; for RSA
    555        units the element is the shared static singleton (must not be
    556        freed), and a still-pending CS unit leaves a NULL slot. */
    557     for (unsigned int i = 0; i < ss->num_bkp; i++)
    558     {
    559       struct DONAU_BatchIssueValues *av = ss->alg_values[i];
    560 
    561       if ( (NULL != av) &&
    562            (av != DONAU_donation_unit_ewv_rsa_singleton ()) )
    563       {
    564         GNUNET_CRYPTO_blinding_input_values_decref (
    565           av->blinding_inputs);
    566         GNUNET_free (av);
    567       }
    568     }
    569   }
    570   GNUNET_free (ss->alg_values);
    571   GNUNET_free (ss->blinding_secrets);
    572   if (NULL != ss->receipts)
    573   {
    574     /* receipts are only signed if the Donau answered 200 */
    575     for (unsigned int i = 0; i < ss->num_bkp; i++)
    576       if (NULL != ss->receipts[i].donation_unit_sig.unblinded_sig)
    577         GNUNET_CRYPTO_unblinded_sig_decref (
    578           ss->receipts[i].donation_unit_sig.unblinded_sig);
    579     GNUNET_free (ss->receipts);
    580   }
    581   if (NULL != ss->bkps)
    582   {
    583     for (unsigned int i = 0; i < ss->num_bkp; i++)
    584       if (NULL != ss->bkps[i].blinded_udi.blinded_message)
    585         GNUNET_CRYPTO_blinded_message_decref (
    586           ss->bkps[i].blinded_udi.blinded_message);
    587     GNUNET_free (ss->bkps);
    588   }
    589   GNUNET_free (ss);
    590 }
    591 
    592 
    593 /**
    594  * Offer internal data from a "deposit" CMD, to other commands.
    595  *
    596  * @param cls closure.
    597  * @param[out] ret result.
    598  * @param trait name of the trait.
    599  * @param index index number of the object to offer.
    600  * @return #GNUNET_OK on success.
    601  */
    602 static enum GNUNET_GenericReturnValue
    603 issue_receipts_traits (void *cls,
    604                        const void **ret,
    605                        const char *trait,
    606                        unsigned int index)
    607 {
    608   struct StatusState *ss = cls;
    609   struct TALER_TESTING_Trait traits[] = {
    610     TALER_TESTING_make_trait_donor_salt (ss->donor_salt),
    611     TALER_TESTING_make_trait_donor_tax_id (ss->donor_tax_id),
    612     TALER_TESTING_make_trait_salted_tax_id_hash (
    613       (const struct DONAU_HashDonorTaxId *) &ss->h_donor_tax_id),
    614     TALER_TESTING_make_trait_donation_receipts (
    615       (const struct DONAU_DonationReceipt **) &ss->receipts),
    616     TALER_TESTING_make_trait_number_receipts (&ss->num_bkp),
    617     TALER_TESTING_trait_end ()
    618   };
    619 
    620   return TALER_TESTING_get_trait (traits,
    621                                   ret,
    622                                   trait,
    623                                   index);
    624 }
    625 
    626 
    627 struct TALER_TESTING_Command
    628 TALER_TESTING_cmd_issue_receipts (const char *label,
    629                                   const char *charity_reference,
    630                                   bool uses_cs,
    631                                   uint32_t year,
    632                                   const char *donor_tax_id,
    633                                   const char *salt,
    634                                   const char *issue_amount,
    635                                   unsigned int expected_response_code)
    636 {
    637   struct StatusState *ss;
    638 
    639   ss = GNUNET_new (struct StatusState);
    640   ss->year = year;
    641   ss->charity_reference = charity_reference;
    642   ss->expected_response_code = expected_response_code;
    643   ss->uses_cs = uses_cs;
    644   ss->donor_salt = (const char*) salt;
    645   ss->donor_tax_id = (const char*) donor_tax_id;
    646   if (GNUNET_OK !=
    647       TALER_string_to_amount (issue_amount,
    648                               &ss->amount))
    649   {
    650     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    651                 "Failed to parse amount `%s' at %s\n",
    652                 issue_amount,
    653                 label);
    654     GNUNET_assert (0);
    655   }
    656 
    657   if (! DONAU_compute_salted_tax_id_hash (donor_tax_id,
    658                                           salt,
    659                                           ss->h_donor_tax_id.hash))
    660   {
    661     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    662                 "Hash was not received");
    663     GNUNET_assert (0);
    664   }
    665 
    666   {
    667     struct TALER_TESTING_Command cmd = {
    668       .cls = ss,
    669       .label = label,
    670       .run = &status_run,
    671       .cleanup = &cleanup,
    672       .traits = &issue_receipts_traits
    673     };
    674 
    675     return cmd;
    676   }
    677 }