Line data Source code
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 2 : handle_deposit_confirmation_finished (void *cls,
79 : long response_code,
80 : const void *djson)
81 : {
82 2 : const json_t *json = djson;
83 2 : struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls;
84 2 : struct TALER_AUDITOR_DepositConfirmationResponse dcr = {
85 : .hr.reply = json,
86 2 : .hr.http_status = (unsigned int) response_code
87 : };
88 :
89 2 : dh->job = NULL;
90 2 : switch (response_code)
91 : {
92 0 : case 0:
93 0 : dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
94 0 : break;
95 2 : case MHD_HTTP_OK:
96 2 : dcr.hr.ec = TALER_EC_NONE;
97 2 : break;
98 0 : case MHD_HTTP_BAD_REQUEST:
99 0 : dcr.hr.ec = TALER_JSON_get_error_code (json);
100 0 : 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 0 : break;
104 0 : case MHD_HTTP_FORBIDDEN:
105 0 : dcr.hr.ec = TALER_JSON_get_error_code (json);
106 0 : 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 0 : break;
111 0 : case MHD_HTTP_NOT_FOUND:
112 0 : dcr.hr.ec = TALER_JSON_get_error_code (json);
113 0 : 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 0 : break;
117 0 : case MHD_HTTP_GONE:
118 0 : dcr.hr.ec = TALER_JSON_get_error_code (json);
119 0 : 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 0 : break;
124 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
125 0 : dcr.hr.ec = TALER_JSON_get_error_code (json);
126 0 : 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 0 : break;
130 0 : default:
131 : /* unexpected response code */
132 0 : dcr.hr.ec = TALER_JSON_get_error_code (json);
133 0 : dcr.hr.hint = TALER_JSON_get_error_hint (json);
134 0 : 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 0 : break;
139 : }
140 2 : dh->cb (dh->cb_cls,
141 : &dcr);
142 2 : TALER_AUDITOR_deposit_confirmation_cancel (dh);
143 2 : }
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 2 : 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 2 : {
189 2 : if (GNUNET_OK !=
190 2 : 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 0 : GNUNET_break_op (0);
205 0 : TALER_LOG_WARNING (
206 : "Invalid signature on /deposit-confirmation request!\n");
207 : {
208 0 : TALER_LOG_DEBUG ("... amount_without_fee was %s\n",
209 : TALER_amount2s (amount_without_fee));
210 : }
211 0 : return GNUNET_SYSERR;
212 : }
213 :
214 2 : if (GNUNET_OK !=
215 2 : 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 0 : GNUNET_break (0);
224 0 : TALER_LOG_WARNING ("Invalid signature on exchange signing key!\n");
225 0 : return GNUNET_SYSERR;
226 : }
227 2 : if (GNUNET_TIME_absolute_is_past (ep_end.abs_time))
228 : {
229 0 : GNUNET_break (0);
230 0 : TALER_LOG_WARNING ("Exchange signing key is no longer valid!\n");
231 0 : return GNUNET_SYSERR;
232 : }
233 2 : return GNUNET_OK;
234 : }
235 :
236 :
237 : struct TALER_AUDITOR_DepositConfirmationHandle *
238 2 : 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 2 : {
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 2 : if (0 == num_coins)
271 : {
272 0 : GNUNET_break (0);
273 0 : return NULL;
274 : }
275 2 : if (GNUNET_OK !=
276 2 : 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 0 : GNUNET_break_op (0);
295 0 : return NULL;
296 : }
297 2 : jcoin_sigs = json_array ();
298 2 : GNUNET_assert (NULL != jcoin_sigs);
299 2 : jcoin_pubs = json_array ();
300 2 : GNUNET_assert (NULL != jcoin_pubs);
301 4 : for (unsigned int i = 0; i<num_coins; i++)
302 : {
303 2 : GNUNET_assert (0 ==
304 : json_array_append_new (jcoin_sigs,
305 : GNUNET_JSON_from_data_auto (
306 : coin_sigs[i])));
307 2 : 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 2 : = 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 2 : dh = GNUNET_new (struct TALER_AUDITOR_DepositConfirmationHandle);
350 2 : dh->cb = cb;
351 2 : dh->cb_cls = cb_cls;
352 2 : dh->url = TALER_url_join (url,
353 : "deposit-confirmation",
354 : NULL);
355 2 : if (NULL == dh->url)
356 : {
357 0 : GNUNET_free (dh);
358 0 : return NULL;
359 : }
360 2 : eh = TALER_AUDITOR_curl_easy_get_ (dh->url);
361 4 : if ( (NULL == eh) ||
362 : (CURLE_OK !=
363 2 : curl_easy_setopt (eh,
364 : CURLOPT_CUSTOMREQUEST,
365 2 : "PUT")) ||
366 : (GNUNET_OK !=
367 2 : TALER_curl_easy_post (&dh->ctx,
368 : eh,
369 : deposit_confirmation_obj)) )
370 : {
371 0 : GNUNET_break (0);
372 0 : if (NULL != eh)
373 0 : curl_easy_cleanup (eh);
374 0 : json_decref (deposit_confirmation_obj);
375 0 : GNUNET_free (dh->url);
376 0 : GNUNET_free (dh);
377 0 : return NULL;
378 : }
379 2 : json_decref (deposit_confirmation_obj);
380 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381 : "URL for deposit-confirmation: `%s'\n",
382 : dh->url);
383 4 : dh->job = GNUNET_CURL_job_add2 (ctx,
384 : eh,
385 2 : dh->ctx.headers,
386 : &handle_deposit_confirmation_finished,
387 : dh);
388 : {
389 : /* Disable 100 continue processing */
390 : struct curl_slist *x_headers;
391 :
392 2 : x_headers = curl_slist_append (NULL,
393 : "Expect:");
394 2 : GNUNET_CURL_extend_headers (dh->job,
395 : x_headers);
396 2 : curl_slist_free_all (x_headers);
397 : }
398 2 : return dh;
399 : }
400 :
401 :
402 : void
403 2 : TALER_AUDITOR_deposit_confirmation_cancel (
404 : struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation)
405 : {
406 2 : if (NULL != deposit_confirmation->job)
407 : {
408 0 : GNUNET_CURL_job_cancel (deposit_confirmation->job);
409 0 : deposit_confirmation->job = NULL;
410 : }
411 2 : GNUNET_free (deposit_confirmation->url);
412 2 : TALER_curl_easy_post_finished (&deposit_confirmation->ctx);
413 2 : GNUNET_free (deposit_confirmation);
414 2 : }
415 :
416 :
417 : /* end of auditor_api_put_deposit_confirmation.c */
|