auditor_api_put_deposit_confirmation.c (14647B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/auditor_api_put_deposit_confirmation.c 19 * @brief Implementation of the /deposit request of the auditor's HTTP API 20 * @author Christian Grothoff 21 */ 22 #include <jansson.h> 23 #include <microhttpd.h> /* just for HTTP status codes */ 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_json_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "taler/taler_util.h" 28 #include "taler/taler_curl_lib.h" 29 #include "taler/taler_json_lib.h" 30 #include "taler/taler_auditor_service.h" 31 #include "auditor_api_curl_defaults.h" 32 33 34 /** 35 * @brief A DepositConfirmation Handle 36 */ 37 struct TALER_AUDITOR_DepositConfirmationHandle 38 { 39 40 /** 41 * The url for this request. 42 */ 43 char *url; 44 45 /** 46 * Context for #TEH_curl_easy_post(). Keeps the data that must 47 * persist for Curl to make the upload. 48 */ 49 struct TALER_CURL_PostContext ctx; 50 51 /** 52 * Handle for the request. 53 */ 54 struct GNUNET_CURL_Job *job; 55 56 /** 57 * Function to call with the result. 58 */ 59 TALER_AUDITOR_DepositConfirmationResultCallback cb; 60 61 /** 62 * Closure for @a cb. 63 */ 64 void *cb_cls; 65 66 }; 67 68 69 /** 70 * Function called when we're done processing the 71 * HTTP /deposit-confirmation request. 72 * 73 * @param cls the `struct TALER_AUDITOR_DepositConfirmationHandle` 74 * @param response_code HTTP response code, 0 on error 75 * @param djson parsed JSON result, NULL on error 76 */ 77 static void 78 handle_deposit_confirmation_finished (void *cls, 79 long response_code, 80 const void *djson) 81 { 82 const json_t *json = djson; 83 struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls; 84 struct TALER_AUDITOR_DepositConfirmationResponse dcr = { 85 .hr.reply = json, 86 .hr.http_status = (unsigned int) response_code 87 }; 88 89 dh->job = NULL; 90 switch (response_code) 91 { 92 case 0: 93 dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 94 break; 95 case MHD_HTTP_OK: 96 dcr.hr.ec = TALER_EC_NONE; 97 break; 98 case MHD_HTTP_BAD_REQUEST: 99 dcr.hr.ec = TALER_JSON_get_error_code (json); 100 dcr.hr.hint = TALER_JSON_get_error_hint (json); 101 /* This should never happen, either us or the auditor is buggy 102 (or API version conflict); just pass JSON reply to the application */ 103 break; 104 case MHD_HTTP_FORBIDDEN: 105 dcr.hr.ec = TALER_JSON_get_error_code (json); 106 dcr.hr.hint = TALER_JSON_get_error_hint (json); 107 /* Nothing really to verify, auditor says one of the signatures is 108 invalid; as we checked them, this should never happen, we 109 should pass the JSON reply to the application */ 110 break; 111 case MHD_HTTP_NOT_FOUND: 112 dcr.hr.ec = TALER_JSON_get_error_code (json); 113 dcr.hr.hint = TALER_JSON_get_error_hint (json); 114 /* Nothing really to verify, this should never 115 happen, we should pass the JSON reply to the application */ 116 break; 117 case MHD_HTTP_GONE: 118 dcr.hr.ec = TALER_JSON_get_error_code (json); 119 dcr.hr.hint = TALER_JSON_get_error_hint (json); 120 /* Nothing really to verify, auditor says one of the signatures is 121 invalid; as we checked them, this should never happen, we 122 should pass the JSON reply to the application */ 123 break; 124 case MHD_HTTP_INTERNAL_SERVER_ERROR: 125 dcr.hr.ec = TALER_JSON_get_error_code (json); 126 dcr.hr.hint = TALER_JSON_get_error_hint (json); 127 /* Server had an internal issue; we should retry, but this API 128 leaves this to the application */ 129 break; 130 default: 131 /* unexpected response code */ 132 dcr.hr.ec = TALER_JSON_get_error_code (json); 133 dcr.hr.hint = TALER_JSON_get_error_hint (json); 134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 135 "Unexpected response code %u/%d for auditor deposit confirmation\n", 136 (unsigned int) response_code, 137 dcr.hr.ec); 138 break; 139 } 140 dh->cb (dh->cb_cls, 141 &dcr); 142 TALER_AUDITOR_deposit_confirmation_cancel (dh); 143 } 144 145 146 /** 147 * Verify signature information about the deposit-confirmation. 148 * 149 * @param h_wire hash of merchant wire details 150 * @param h_policy hash over the policy extension, if any 151 * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor) 152 * @param exchange_timestamp timestamp when the deposit was received by the wallet 153 * @param wire_deadline by what time must the amount be wired to the merchant 154 * @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline 155 * @param amount_without_fee the amount confirmed to be wired by the exchange to the merchant 156 * @param num_coins number of coins involved 157 * @param coin_sigs array of @a num_coins coin signatures 158 * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) 159 * @param exchange_sig the signature made with purpose #TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT 160 * @param exchange_pub the public key of the exchange that matches @a exchange_sig 161 * @param master_pub master public key of the exchange 162 * @param ep_start when does @a exchange_pub validity start 163 * @param ep_expire when does @a exchange_pub usage end 164 * @param ep_end when does @a exchange_pub legal validity end 165 * @param master_sig master signature affirming validity of @a exchange_pub 166 * @return #GNUNET_OK if signatures are OK, #GNUNET_SYSERR if not 167 */ 168 static enum GNUNET_GenericReturnValue 169 verify_signatures ( 170 const struct TALER_MerchantWireHashP *h_wire, 171 const struct TALER_ExtensionPolicyHashP *h_policy, 172 const struct TALER_PrivateContractHashP *h_contract_terms, 173 struct GNUNET_TIME_Timestamp exchange_timestamp, 174 struct GNUNET_TIME_Timestamp wire_deadline, 175 struct GNUNET_TIME_Timestamp refund_deadline, 176 const struct TALER_Amount *amount_without_fee, 177 unsigned int num_coins, 178 const struct TALER_CoinSpendSignatureP *coin_sigs[ 179 static num_coins], 180 const struct TALER_MerchantPublicKeyP *merchant_pub, 181 const struct TALER_ExchangePublicKeyP *exchange_pub, 182 const struct TALER_ExchangeSignatureP *exchange_sig, 183 const struct TALER_MasterPublicKeyP *master_pub, 184 struct GNUNET_TIME_Timestamp ep_start, 185 struct GNUNET_TIME_Timestamp ep_expire, 186 struct GNUNET_TIME_Timestamp ep_end, 187 const struct TALER_MasterSignatureP *master_sig) 188 { 189 if (GNUNET_OK != 190 TALER_exchange_online_deposit_confirmation_verify ( 191 h_contract_terms, 192 h_wire, 193 h_policy, 194 exchange_timestamp, 195 wire_deadline, 196 refund_deadline, 197 amount_without_fee, 198 num_coins, 199 coin_sigs, 200 merchant_pub, 201 exchange_pub, 202 exchange_sig)) 203 { 204 GNUNET_break_op (0); 205 TALER_LOG_WARNING ( 206 "Invalid signature on /deposit-confirmation request!\n"); 207 { 208 TALER_LOG_DEBUG ("... amount_without_fee was %s\n", 209 TALER_amount2s (amount_without_fee)); 210 } 211 return GNUNET_SYSERR; 212 } 213 214 if (GNUNET_OK != 215 TALER_exchange_offline_signkey_validity_verify ( 216 exchange_pub, 217 ep_start, 218 ep_expire, 219 ep_end, 220 master_pub, 221 master_sig)) 222 { 223 GNUNET_break (0); 224 TALER_LOG_WARNING ("Invalid signature on exchange signing key!\n"); 225 return GNUNET_SYSERR; 226 } 227 if (GNUNET_TIME_absolute_is_past (ep_end.abs_time)) 228 { 229 GNUNET_break (0); 230 TALER_LOG_WARNING ("Exchange signing key is no longer valid!\n"); 231 return GNUNET_SYSERR; 232 } 233 return GNUNET_OK; 234 } 235 236 237 struct TALER_AUDITOR_DepositConfirmationHandle * 238 TALER_AUDITOR_deposit_confirmation ( 239 struct GNUNET_CURL_Context *ctx, 240 const char *url, 241 const struct TALER_MerchantWireHashP *h_wire, 242 const struct TALER_ExtensionPolicyHashP *h_policy, 243 const struct TALER_PrivateContractHashP *h_contract_terms, 244 struct GNUNET_TIME_Timestamp exchange_timestamp, 245 struct GNUNET_TIME_Timestamp wire_deadline, 246 struct GNUNET_TIME_Timestamp refund_deadline, 247 const struct TALER_Amount *total_without_fee, 248 unsigned int num_coins, 249 const struct TALER_CoinSpendPublicKeyP *coin_pubs[ 250 static num_coins], 251 const struct TALER_CoinSpendSignatureP *coin_sigs[ 252 static num_coins], 253 const struct TALER_MerchantPublicKeyP *merchant_pub, 254 const struct TALER_ExchangePublicKeyP *exchange_pub, 255 const struct TALER_ExchangeSignatureP *exchange_sig, 256 const struct TALER_MasterPublicKeyP *master_pub, 257 struct GNUNET_TIME_Timestamp ep_start, 258 struct GNUNET_TIME_Timestamp ep_expire, 259 struct GNUNET_TIME_Timestamp ep_end, 260 const struct TALER_MasterSignatureP *master_sig, 261 TALER_AUDITOR_DepositConfirmationResultCallback cb, 262 void *cb_cls) 263 { 264 struct TALER_AUDITOR_DepositConfirmationHandle *dh; 265 json_t *deposit_confirmation_obj; 266 CURL *eh; 267 json_t *jcoin_sigs; 268 json_t *jcoin_pubs; 269 270 if (0 == num_coins) 271 { 272 GNUNET_break (0); 273 return NULL; 274 } 275 if (GNUNET_OK != 276 verify_signatures (h_wire, 277 h_policy, 278 h_contract_terms, 279 exchange_timestamp, 280 wire_deadline, 281 refund_deadline, 282 total_without_fee, 283 num_coins, 284 coin_sigs, 285 merchant_pub, 286 exchange_pub, 287 exchange_sig, 288 master_pub, 289 ep_start, 290 ep_expire, 291 ep_end, 292 master_sig)) 293 { 294 GNUNET_break_op (0); 295 return NULL; 296 } 297 jcoin_sigs = json_array (); 298 GNUNET_assert (NULL != jcoin_sigs); 299 jcoin_pubs = json_array (); 300 GNUNET_assert (NULL != jcoin_pubs); 301 for (unsigned int i = 0; i<num_coins; i++) 302 { 303 GNUNET_assert (0 == 304 json_array_append_new (jcoin_sigs, 305 GNUNET_JSON_from_data_auto ( 306 coin_sigs[i]))); 307 GNUNET_assert (0 == 308 json_array_append_new (jcoin_pubs, 309 GNUNET_JSON_from_data_auto ( 310 coin_pubs[i]))); 311 } 312 deposit_confirmation_obj 313 = GNUNET_JSON_PACK ( 314 GNUNET_JSON_pack_data_auto ("h_wire", 315 h_wire), 316 GNUNET_JSON_pack_data_auto ("h_policy", 317 h_policy), 318 GNUNET_JSON_pack_data_auto ("h_contract_terms", 319 h_contract_terms), 320 GNUNET_JSON_pack_timestamp ("exchange_timestamp", 321 exchange_timestamp), 322 GNUNET_JSON_pack_allow_null ( 323 GNUNET_JSON_pack_timestamp ("refund_deadline", 324 refund_deadline)), 325 GNUNET_JSON_pack_timestamp ("wire_deadline", 326 wire_deadline), 327 TALER_JSON_pack_amount ("total_without_fee", 328 total_without_fee), 329 GNUNET_JSON_pack_array_steal ("coin_pubs", 330 jcoin_pubs), 331 GNUNET_JSON_pack_array_steal ("coin_sigs", 332 jcoin_sigs), 333 GNUNET_JSON_pack_data_auto ("merchant_pub", 334 merchant_pub), 335 GNUNET_JSON_pack_data_auto ("exchange_sig", 336 exchange_sig), 337 GNUNET_JSON_pack_data_auto ("master_pub", 338 master_pub), 339 GNUNET_JSON_pack_timestamp ("ep_start", 340 ep_start), 341 GNUNET_JSON_pack_timestamp ("ep_expire", 342 ep_expire), 343 GNUNET_JSON_pack_timestamp ("ep_end", 344 ep_end), 345 GNUNET_JSON_pack_data_auto ("master_sig", 346 master_sig), 347 GNUNET_JSON_pack_data_auto ("exchange_pub", 348 exchange_pub)); 349 dh = GNUNET_new (struct TALER_AUDITOR_DepositConfirmationHandle); 350 dh->cb = cb; 351 dh->cb_cls = cb_cls; 352 dh->url = TALER_url_join (url, 353 "deposit-confirmation", 354 NULL); 355 if (NULL == dh->url) 356 { 357 GNUNET_free (dh); 358 return NULL; 359 } 360 eh = TALER_AUDITOR_curl_easy_get_ (dh->url); 361 if ( (NULL == eh) || 362 (CURLE_OK != 363 curl_easy_setopt (eh, 364 CURLOPT_CUSTOMREQUEST, 365 "PUT")) || 366 (GNUNET_OK != 367 TALER_curl_easy_post (&dh->ctx, 368 eh, 369 deposit_confirmation_obj)) ) 370 { 371 GNUNET_break (0); 372 if (NULL != eh) 373 curl_easy_cleanup (eh); 374 json_decref (deposit_confirmation_obj); 375 GNUNET_free (dh->url); 376 GNUNET_free (dh); 377 return NULL; 378 } 379 json_decref (deposit_confirmation_obj); 380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 381 "URL for deposit-confirmation: `%s'\n", 382 dh->url); 383 dh->job = GNUNET_CURL_job_add2 (ctx, 384 eh, 385 dh->ctx.headers, 386 &handle_deposit_confirmation_finished, 387 dh); 388 { 389 /* Disable 100 continue processing */ 390 struct curl_slist *x_headers; 391 392 x_headers = curl_slist_append (NULL, 393 "Expect:"); 394 GNUNET_CURL_extend_headers (dh->job, 395 x_headers); 396 curl_slist_free_all (x_headers); 397 } 398 return dh; 399 } 400 401 402 void 403 TALER_AUDITOR_deposit_confirmation_cancel ( 404 struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation) 405 { 406 if (NULL != deposit_confirmation->job) 407 { 408 GNUNET_CURL_job_cancel (deposit_confirmation->job); 409 deposit_confirmation->job = NULL; 410 } 411 GNUNET_free (deposit_confirmation->url); 412 TALER_curl_easy_post_finished (&deposit_confirmation->ctx); 413 GNUNET_free (deposit_confirmation); 414 } 415 416 417 /* end of auditor_api_put_deposit_confirmation.c */