exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-httpd_common_deposit.c (9959B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-exchange-httpd_common_deposit.c
     18  * @brief shared logic for handling deposited coins
     19  * @author Christian Grothoff
     20  */
     21 #include "taler-exchange-httpd_common_deposit.h"
     22 #include "taler-exchange-httpd.h"
     23 #include "taler-exchange-httpd_get-keys.h"
     24 
     25 
     26 enum GNUNET_GenericReturnValue
     27 TEH_common_purse_deposit_parse_coin (
     28   struct MHD_Connection *connection,
     29   struct TEH_PurseDepositedCoin *coin,
     30   const json_t *jcoin)
     31 {
     32   struct GNUNET_JSON_Specification spec[] = {
     33     TALER_JSON_spec_amount ("amount",
     34                             TEH_currency,
     35                             &coin->amount),
     36     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     37                                  &coin->cpi.denom_pub_hash),
     38     TALER_JSON_spec_denom_sig ("ub_sig",
     39                                &coin->cpi.denom_sig),
     40     GNUNET_JSON_spec_mark_optional (
     41       GNUNET_JSON_spec_fixed_auto ("attest",
     42                                    &coin->attest),
     43       &coin->no_attest),
     44     GNUNET_JSON_spec_mark_optional (
     45       TALER_JSON_spec_age_commitment ("age_commitment",
     46                                       &coin->age_commitment),
     47       &coin->cpi.no_age_commitment),
     48     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     49                                  &coin->coin_sig),
     50     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     51                                  &coin->cpi.coin_pub),
     52     GNUNET_JSON_spec_end ()
     53   };
     54 
     55   memset (coin,
     56           0,
     57           sizeof (*coin));
     58   coin->cpi.no_age_commitment = true;
     59   coin->no_attest = true;
     60   {
     61     enum GNUNET_GenericReturnValue res;
     62 
     63     res = TALER_MHD_parse_json_data (connection,
     64                                      jcoin,
     65                                      spec);
     66     if (GNUNET_OK != res)
     67       return res;
     68   }
     69 
     70   /* check denomination exists and is valid */
     71   {
     72     struct TEH_DenominationKey *dk;
     73     enum MHD_Result mret;
     74 
     75     dk = TEH_keys_denomination_by_hash (&coin->cpi.denom_pub_hash,
     76                                         connection,
     77                                         &mret);
     78     if (NULL == dk)
     79     {
     80       GNUNET_JSON_parse_free (spec);
     81       return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR;
     82     }
     83     if (! coin->cpi.no_age_commitment)
     84     {
     85       coin->age_commitment.mask = dk->meta.age_mask;
     86       TALER_age_commitment_hash (&coin->age_commitment,
     87                                  &coin->cpi.h_age_commitment);
     88     }
     89     if (0 > TALER_amount_cmp (&dk->meta.value,
     90                               &coin->amount))
     91     {
     92       GNUNET_break_op (0);
     93       GNUNET_JSON_parse_free (spec);
     94       return (MHD_YES ==
     95               TALER_MHD_reply_with_error (connection,
     96                                           MHD_HTTP_BAD_REQUEST,
     97                                           TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
     98                                           NULL))
     99              ? GNUNET_NO : GNUNET_SYSERR;
    100     }
    101     if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
    102     {
    103       /* This denomination is past the expiration time for deposits */
    104       GNUNET_JSON_parse_free (spec);
    105       return (MHD_YES ==
    106               TEH_RESPONSE_reply_expired_denom_pub_hash (
    107                 connection,
    108                 &coin->cpi.denom_pub_hash,
    109                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
    110                 "PURSE CREATE"))
    111              ? GNUNET_NO : GNUNET_SYSERR;
    112     }
    113     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
    114     {
    115       /* This denomination is not yet valid */
    116       GNUNET_JSON_parse_free (spec);
    117       return (MHD_YES ==
    118               TEH_RESPONSE_reply_expired_denom_pub_hash (
    119                 connection,
    120                 &coin->cpi.denom_pub_hash,
    121                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
    122                 "PURSE CREATE"))
    123              ? GNUNET_NO : GNUNET_SYSERR;
    124     }
    125     if (dk->recoup_possible)
    126     {
    127       /* This denomination has been revoked */
    128       GNUNET_JSON_parse_free (spec);
    129       return (MHD_YES ==
    130               TEH_RESPONSE_reply_expired_denom_pub_hash (
    131                 connection,
    132                 &coin->cpi.denom_pub_hash,
    133                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
    134                 "PURSE CREATE"))
    135              ? GNUNET_NO : GNUNET_SYSERR;
    136     }
    137     if (dk->denom_pub.bsign_pub_key->cipher !=
    138         coin->cpi.denom_sig.unblinded_sig->cipher)
    139     {
    140       /* denomination cipher and denomination signature cipher not the same */
    141       GNUNET_JSON_parse_free (spec);
    142       return (MHD_YES ==
    143               TALER_MHD_reply_with_error (connection,
    144                                           MHD_HTTP_BAD_REQUEST,
    145                                           TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
    146                                           NULL))
    147              ? GNUNET_NO : GNUNET_SYSERR;
    148     }
    149 
    150     coin->deposit_fee = dk->meta.fees.deposit;
    151     if (0 < TALER_amount_cmp (&coin->deposit_fee,
    152                               &coin->amount))
    153     {
    154       GNUNET_break_op (0);
    155       GNUNET_JSON_parse_free (spec);
    156       return (MHD_YES ==
    157               TALER_MHD_reply_with_error (connection,
    158                                           MHD_HTTP_BAD_REQUEST,
    159                                           TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
    160                                           NULL))
    161              ? GNUNET_NO : GNUNET_SYSERR;
    162     }
    163     GNUNET_assert (0 <=
    164                    TALER_amount_subtract (&coin->amount_minus_fee,
    165                                           &coin->amount,
    166                                           &coin->deposit_fee));
    167 
    168     /* check coin signature */
    169     switch (dk->denom_pub.bsign_pub_key->cipher)
    170     {
    171     case GNUNET_CRYPTO_BSA_RSA:
    172       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++;
    173       break;
    174     case GNUNET_CRYPTO_BSA_CS:
    175       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++;
    176       break;
    177     default:
    178       break;
    179     }
    180     if (GNUNET_YES !=
    181         TALER_test_coin_valid (&coin->cpi,
    182                                &dk->denom_pub))
    183     {
    184       TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
    185       GNUNET_JSON_parse_free (spec);
    186       return (MHD_YES ==
    187               TALER_MHD_reply_with_error (connection,
    188                                           MHD_HTTP_FORBIDDEN,
    189                                           TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
    190                                           NULL))
    191              ? GNUNET_NO : GNUNET_SYSERR;
    192     }
    193   }
    194   return GNUNET_OK;
    195 }
    196 
    197 
    198 enum GNUNET_GenericReturnValue
    199 TEH_common_deposit_check_purse_deposit (
    200   struct MHD_Connection *connection,
    201   const struct TEH_PurseDepositedCoin *coin,
    202   const struct TALER_PurseContractPublicKeyP *purse_pub,
    203   uint32_t min_age)
    204 {
    205   if (GNUNET_OK !=
    206       TALER_wallet_purse_deposit_verify (TEH_base_url,
    207                                          purse_pub,
    208                                          &coin->amount,
    209                                          &coin->cpi.denom_pub_hash,
    210                                          &coin->cpi.h_age_commitment,
    211                                          &coin->cpi.coin_pub,
    212                                          &coin->coin_sig))
    213   {
    214     TALER_LOG_WARNING (
    215       "Invalid coin signature to deposit into purse\n");
    216     return (MHD_YES ==
    217             TALER_MHD_reply_with_error (connection,
    218                                         MHD_HTTP_FORBIDDEN,
    219                                         TALER_EC_EXCHANGE_PURSE_DEPOSIT_COIN_SIGNATURE_INVALID,
    220                                         TEH_base_url))
    221            ? GNUNET_NO
    222            : GNUNET_SYSERR;
    223   }
    224 
    225   if (0 == min_age)
    226     return GNUNET_OK; /* no need to apply age checks */
    227 
    228   /* Check and verify the age restriction. */
    229   if (coin->no_attest != coin->cpi.no_age_commitment)
    230   {
    231     GNUNET_break_op (0);
    232     return (MHD_YES ==
    233             TALER_MHD_reply_with_error (connection,
    234                                         MHD_HTTP_BAD_REQUEST,
    235                                         TALER_EC_EXCHANGE_PURSE_DEPOSIT_COIN_CONFLICTING_ATTEST_VS_AGE_COMMITMENT,
    236                                         "mismatch of attest and age_commitment")
    237             )
    238            ? GNUNET_NO : GNUNET_SYSERR;
    239   }
    240 
    241   if (coin->cpi.no_age_commitment)
    242     return GNUNET_OK; /* unrestricted coin */
    243 
    244   /* age attestation must be valid */
    245   if (GNUNET_OK !=
    246       TALER_age_commitment_verify (&coin->age_commitment,
    247                                    min_age,
    248                                    &coin->attest))
    249   {
    250     GNUNET_break_op (0);
    251     return (MHD_YES ==
    252             TALER_MHD_reply_with_error (connection,
    253                                         MHD_HTTP_BAD_REQUEST,
    254                                         TALER_EC_EXCHANGE_PURSE_DEPOSIT_COIN_AGE_ATTESTATION_FAILURE,
    255                                         "invalid attest for minimum age"))
    256            ? GNUNET_NO : GNUNET_SYSERR;
    257   }
    258   return GNUNET_OK;
    259 }
    260 
    261 
    262 /**
    263  * Release data structures of @a coin. Note that
    264  * @a coin itself is NOT freed.
    265  *
    266  * @param[in] coin information to release
    267  */
    268 void
    269 TEH_common_purse_deposit_free_coin (struct TEH_PurseDepositedCoin *coin)
    270 {
    271   TALER_denom_sig_free (&coin->cpi.denom_sig);
    272   if (! coin->cpi.no_age_commitment)
    273     GNUNET_free (coin->age_commitment.pubs); /* Only the keys have been allocated */
    274 }