merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.c (7750B)


      1 /*
      2   This file is part of TALER
      3   (C) 2022 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Affero General Public License as
      7   published by the Free Software Foundation; either version 3,
      8   or (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,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file src/backend/taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.c
     22  * @brief implementing PATCH /otp-devices/$ID request handling
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.h"
     27 #include "taler-merchant-httpd_helper.h"
     28 #include <taler/taler_json_lib.h>
     29 #include "merchant-database/update_otp_device.h"
     30 #include "merchant-database/get_otp_device.h"
     31 
     32 
     33 /**
     34  * Does @a alg confirm payments by signing a challenge with a key pair
     35  * generated by the backend?
     36  *
     37  * @param alg algorithm to check
     38  * @return true for the challenge-signature algorithms
     39  */
     40 static bool
     41 is_challenge_alg (enum TALER_MerchantConfirmationAlgorithm alg)
     42 {
     43   return ( (TALER_MCA_ECDSA_CHALLENGE == alg) ||
     44            (TALER_MCA_EDDSA_CHALLENGE == alg) );
     45 }
     46 
     47 
     48 enum MHD_Result
     49 TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh,
     50                                   struct MHD_Connection *connection,
     51                                   struct TMH_HandlerContext *hc)
     52 {
     53   struct TMH_MerchantInstance *mi = hc->instance;
     54   const char *device_id = hc->infix;
     55   struct TALER_MERCHANTDB_OtpDeviceDetails tp = {0};
     56   uint64_t expected_serial;
     57   enum TALER_MerchantConfirmationAlgorithm expected_algorithm;
     58   enum GNUNET_DB_QueryStatus qs;
     59   struct GNUNET_JSON_Specification spec[] = {
     60     GNUNET_JSON_spec_string ("otp_device_description",
     61                              (const char **) &tp.otp_description),
     62     TALER_JSON_spec_otp_type ("otp_algorithm",
     63                               &tp.otp_algorithm),
     64     GNUNET_JSON_spec_mark_optional (
     65       GNUNET_JSON_spec_uint64 ("otp_ctr",
     66                                &tp.otp_ctr),
     67       NULL),
     68     GNUNET_JSON_spec_mark_optional (
     69 
     70       TALER_JSON_spec_otp_key ("otp_key",
     71                                (const char **) &tp.otp_key),
     72       NULL),
     73     GNUNET_JSON_spec_end ()
     74   };
     75 
     76   GNUNET_assert (NULL != mi);
     77   GNUNET_assert (NULL != device_id);
     78   {
     79     enum GNUNET_GenericReturnValue res;
     80 
     81     res = TALER_MHD_parse_json_data (connection,
     82                                      hc->request_body,
     83                                      spec);
     84     if (GNUNET_OK != res)
     85       return (GNUNET_NO == res)
     86              ? MHD_YES
     87              : MHD_NO;
     88   }
     89 
     90   switch (tp.otp_algorithm)
     91   {
     92   case TALER_MCA_ECDSA_CHALLENGE:
     93   case TALER_MCA_EDDSA_CHALLENGE:
     94     /* the key pair belongs to the backend; there is nothing here for a
     95        client to set, and replacing it would silently invalidate every
     96        offline verifier already configured with the public key */
     97     if (NULL != tp.otp_key)
     98     {
     99       GNUNET_break_op (0);
    100       return TALER_MHD_reply_with_error (connection,
    101                                          MHD_HTTP_BAD_REQUEST,
    102                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    103                                          "otp_key");
    104     }
    105     break;
    106   case TALER_MCA_NONE:
    107   case TALER_MCA_WITHOUT_PRICE:
    108   case TALER_MCA_WITH_PRICE:
    109     break;
    110   }
    111 
    112   {
    113     struct TALER_MERCHANTDB_OtpDeviceDetails etp;
    114     bool incompatible = false;
    115 
    116     qs = TALER_MERCHANTDB_get_otp_device (TMH_db,
    117                                           mi->settings.id,
    118                                           device_id,
    119                                           &etp);
    120     switch (qs)
    121     {
    122     case GNUNET_DB_STATUS_HARD_ERROR:
    123     case GNUNET_DB_STATUS_SOFT_ERROR:
    124       GNUNET_break (0);
    125       GNUNET_JSON_parse_free (spec);
    126       return TALER_MHD_reply_with_error (connection,
    127                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    128                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    129                                          "get_otp_device");
    130     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    131       GNUNET_JSON_parse_free (spec);
    132       return TALER_MHD_reply_with_error (connection,
    133                                          MHD_HTTP_NOT_FOUND,
    134                                          TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
    135                                          device_id);
    136     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    137       expected_serial = etp.otp_serial;
    138       expected_algorithm = etp.otp_algorithm;
    139       incompatible = (etp.otp_algorithm != tp.otp_algorithm) &&
    140                      (is_challenge_alg (etp.otp_algorithm) ||
    141                       is_challenge_alg (tp.otp_algorithm));
    142       GNUNET_free (etp.otp_description);
    143       GNUNET_free (etp.otp_key);
    144       GNUNET_free (etp.otp_device_pub);
    145       break;
    146     }
    147     if (incompatible)
    148     {
    149       /* Challenge keys belong to a specific algorithm and cannot be
    150          reused with another challenge algorithm or with TOTP.  Recreate
    151          the device to change its algorithm. */
    152       GNUNET_break_op (0);
    153       GNUNET_JSON_parse_free (spec);
    154       return TALER_MHD_reply_with_error (connection,
    155                                          MHD_HTTP_CONFLICT,
    156                                          TALER_EC_MERCHANT_PRIVATE_PATCH_OTP_DEVICES_CONFLICT,
    157                                          "otp_algorithm");
    158     }
    159   }
    160 
    161   qs = TALER_MERCHANTDB_update_otp_device (TMH_db,
    162                                            mi->settings.id,
    163                                            device_id,
    164                                            expected_serial,
    165                                            expected_algorithm,
    166                                            &tp);
    167   {
    168     enum MHD_Result ret = MHD_NO;
    169 
    170     switch (qs)
    171     {
    172     case GNUNET_DB_STATUS_HARD_ERROR:
    173       GNUNET_break (0);
    174       ret = TALER_MHD_reply_with_error (connection,
    175                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    176                                         TALER_EC_GENERIC_DB_STORE_FAILED,
    177                                         "update_pos");
    178       break;
    179     case GNUNET_DB_STATUS_SOFT_ERROR:
    180       GNUNET_break (0);
    181       ret = TALER_MHD_reply_with_error (connection,
    182                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    183                                         TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    184                                         "unexpected serialization problem");
    185       break;
    186     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    187       /* Validation no longer describes this device.  The conditional
    188          UPDATE left the concurrent writer's device untouched. */
    189       ret = TALER_MHD_reply_with_error (connection,
    190                                         MHD_HTTP_CONFLICT,
    191                                         TALER_EC_MERCHANT_PRIVATE_PATCH_OTP_DEVICES_CONFLICT,
    192                                         "OTP device changed during update");
    193       break;
    194     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    195       ret = TALER_MHD_reply_static (connection,
    196                                     MHD_HTTP_NO_CONTENT,
    197                                     NULL,
    198                                     NULL,
    199                                     0);
    200       break;
    201     }
    202     GNUNET_JSON_parse_free (spec);
    203     return ret;
    204   }
    205 }
    206 
    207 
    208 /* end of taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.c */