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