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
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (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, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/test_exchange_api_revocation.c
21 : * @brief testcase to test key revocation handling via the exchange's HTTP API interface
22 : * @author Sree Harsha Totakura <sreeharsha@totakura.in>
23 : * @author Christian Grothoff
24 : * @author Marcello Stanisci
25 : */
26 : #include "taler/taler_util.h"
27 : #include "taler/taler_json_lib.h"
28 : #include <gnunet/gnunet_util_lib.h>
29 : #include <gnunet/gnunet_testing_lib.h>
30 : #include <microhttpd.h>
31 : #include "taler/taler_bank_service.h"
32 : #include "taler/taler_testing_lib.h"
33 :
34 : /**
35 : * Configuration file we use. One (big) configuration is used
36 : * for the various components for this test.
37 : */
38 : static char *config_file;
39 :
40 : /**
41 : * Our credentials.
42 : */
43 : static struct TALER_TESTING_Credentials cred;
44 :
45 :
46 : /**
47 : * Execute the taler-exchange-wirewatch command with
48 : * our configuration file.
49 : *
50 : * @param label label to use for the command.
51 : */
52 : static struct TALER_TESTING_Command
53 8 : CMD_EXEC_WIREWATCH (const char *label)
54 : {
55 8 : return TALER_TESTING_cmd_exec_wirewatch2 (label,
56 : config_file,
57 : "exchange-account-2");
58 : }
59 :
60 :
61 : /**
62 : * Main function that will tell the interpreter what commands to
63 : * run.
64 : *
65 : * @param cls closure
66 : */
67 : static void
68 2 : run (void *cls,
69 : struct TALER_TESTING_Interpreter *is)
70 : {
71 : struct TALER_TESTING_Command revocation[] = {
72 2 : TALER_TESTING_cmd_run_fakebank ("run-fakebank",
73 2 : cred.cfg,
74 : "exchange-account-2"),
75 2 : TALER_TESTING_cmd_system_start ("start-taler",
76 : config_file,
77 : "-e",
78 : NULL),
79 2 : TALER_TESTING_cmd_get_exchange ("get-exchange",
80 2 : cred.cfg,
81 : NULL,
82 : true,
83 : true),
84 : /**
85 : * Fill reserve with EUR:10.02, as withdraw fee is 1 ct per
86 : * config.
87 : */
88 2 : TALER_TESTING_cmd_admin_add_incoming ("create-reserve-1",
89 : "EUR:10.02",
90 : &cred.ba,
91 : cred.user42_payto),
92 2 : TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1",
93 : "EUR:10.02",
94 : cred.user42_payto,
95 : cred.exchange_payto,
96 : "create-reserve-1"),
97 : /**
98 : * Run wire-watch to trigger the reserve creation.
99 : */
100 2 : CMD_EXEC_WIREWATCH ("wirewatch-4"),
101 : /* Withdraw a 5 EUR coin, at fee of 1 ct */
102 2 : TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-1",
103 : "create-reserve-1",
104 : "EUR:5",
105 : 0, /* age restriction off */
106 : MHD_HTTP_OK),
107 : /* Withdraw another 5 EUR coin, at fee of 1 ct */
108 2 : TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-2",
109 : "create-reserve-1",
110 : "EUR:5",
111 : 0, /* age restriction off */
112 : MHD_HTTP_OK),
113 : /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full)
114 : * (merchant would receive EUR:0.99 due to 1 ct deposit fee) *///
115 2 : TALER_TESTING_cmd_set_var (
116 : "account-priv",
117 : TALER_TESTING_cmd_deposit (
118 : "deposit-partial-fail-kyc",
119 : "withdraw-revocation-coin-1",
120 : 0,
121 : cred.user42_payto,
122 : "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}",
123 2 : GNUNET_TIME_UNIT_ZERO,
124 : "EUR:1",
125 : MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS)),
126 2 : TALER_TESTING_cmd_admin_add_kycauth (
127 : "kyc-auth-transfer",
128 : "EUR:0.01",
129 : &cred.ba,
130 : cred.user42_payto,
131 : "deposit-partial-fail-kyc"),
132 2 : CMD_EXEC_WIREWATCH (
133 : "import-kyc-account-withdraw"),
134 2 : TALER_TESTING_cmd_deposit (
135 : "deposit-partial",
136 : "withdraw-revocation-coin-1",
137 : 0,
138 : cred.user42_payto,
139 : "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:2\"}]}",
140 2 : GNUNET_TIME_UNIT_ZERO,
141 : "EUR:1",
142 : MHD_HTTP_OK),
143 : /* Deposit another coin in full */
144 2 : TALER_TESTING_cmd_deposit (
145 : "deposit-full",
146 : "withdraw-revocation-coin-2",
147 : 0,
148 : cred.user42_payto,
149 : "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:5\"}]}",
150 2 : GNUNET_TIME_UNIT_ZERO,
151 : "EUR:5",
152 : MHD_HTTP_OK),
153 : /**
154 : * Melt SOME of the rest of the coin's value
155 : * (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13)
156 : */
157 2 : TALER_TESTING_cmd_melt ("refresh-melt-1",
158 : "withdraw-revocation-coin-1",
159 : MHD_HTTP_OK,
160 : NULL),
161 : /**
162 : * Complete (successful) melt operation, and withdraw the coins
163 : */
164 2 : TALER_TESTING_cmd_melt_reveal ("refresh-reveal-1",
165 : "refresh-melt-1",
166 : MHD_HTTP_OK),
167 : /* Try to recoup before it's allowed */
168 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-not-allowed",
169 : MHD_HTTP_GONE,
170 : "refresh-reveal-1#0",
171 : "refresh-melt-1",
172 : "EUR:0.1"),
173 : /* Make refreshed coin invalid */
174 2 : TALER_TESTING_cmd_revoke ("revoke-2-EUR:5",
175 : MHD_HTTP_OK,
176 : "refresh-melt-1",
177 : config_file),
178 : /* Also make fully spent coin invalid (should be same denom) */
179 2 : TALER_TESTING_cmd_revoke ("revoke-2-EUR:5",
180 : MHD_HTTP_OK,
181 : "withdraw-revocation-coin-2",
182 : config_file),
183 : /* Refund fully spent coin (which should fail) */
184 2 : TALER_TESTING_cmd_recoup ("recoup-fully-spent",
185 : MHD_HTTP_CONFLICT,
186 : "withdraw-revocation-coin-2",
187 : "EUR:0.1"),
188 : /* Refund coin to original coin */
189 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1a",
190 : MHD_HTTP_OK,
191 : "refresh-reveal-1#0",
192 : "refresh-melt-1",
193 : "EUR:1"),
194 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1b",
195 : MHD_HTTP_OK,
196 : "refresh-reveal-1#1",
197 : "refresh-melt-1",
198 : "EUR:1"),
199 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1c",
200 : MHD_HTTP_OK,
201 : "refresh-reveal-1#2",
202 : "refresh-melt-1",
203 : "EUR:1"),
204 : /* Repeat recoup to test idempotency */
205 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1c",
206 : MHD_HTTP_OK,
207 : "refresh-reveal-1#2",
208 : "refresh-melt-1",
209 : "EUR:1"),
210 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1c",
211 : MHD_HTTP_OK,
212 : "refresh-reveal-1#2",
213 : "refresh-melt-1",
214 : "EUR:1"),
215 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1c",
216 : MHD_HTTP_OK,
217 : "refresh-reveal-1#2",
218 : "refresh-melt-1",
219 : "EUR:1"),
220 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-1c",
221 : MHD_HTTP_OK,
222 : "refresh-reveal-1#2",
223 : "refresh-melt-1",
224 : "EUR:1"),
225 : /* Now we have EUR:3.83 EUR back after 3x EUR:1 in recoups */
226 : /* Melt original coin AGAIN, but only create one 0.1 EUR coin;
227 : This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
228 : leaving EUR:3.69. */
229 2 : TALER_TESTING_cmd_melt ("refresh-melt-2",
230 : "withdraw-revocation-coin-1",
231 : MHD_HTTP_OK,
232 : "EUR:0.1",
233 : NULL),
234 : /**
235 : * Complete (successful) melt operation, and withdraw the coins
236 : */
237 2 : TALER_TESTING_cmd_melt_reveal ("refresh-reveal-2",
238 : "refresh-melt-2",
239 : MHD_HTTP_OK),
240 : /* Revokes refreshed EUR:0.1 coin */
241 2 : TALER_TESTING_cmd_revoke ("revoke-3-EUR:0.1",
242 : MHD_HTTP_OK,
243 : "refresh-reveal-2",
244 : config_file),
245 : /* Revoke also original coin denomination */
246 2 : TALER_TESTING_cmd_revoke ("revoke-4-EUR:5",
247 : MHD_HTTP_OK,
248 : "withdraw-revocation-coin-1",
249 : config_file),
250 : /* Refund coin EUR:0.1 to original coin, creating zombie! */
251 2 : TALER_TESTING_cmd_recoup_refresh ("recoup-2",
252 : MHD_HTTP_OK,
253 : "refresh-reveal-2",
254 : "refresh-melt-2",
255 : "EUR:0.1"),
256 : /* Due to recoup, original coin is now at EUR:3.79 */
257 : /* Refund original (now zombie) coin to reserve */
258 2 : TALER_TESTING_cmd_recoup ("recoup-3",
259 : MHD_HTTP_OK,
260 : "withdraw-revocation-coin-1",
261 : "EUR:3.79"),
262 : /* Check the money is back with the reserve */
263 2 : TALER_TESTING_cmd_status ("recoup-reserve-status-1",
264 : "create-reserve-1",
265 : "EUR:3.79",
266 : MHD_HTTP_OK),
267 : /* Batch recoup: three coins from one withdraw operation, two of
268 : them recouped in one request; the histories must show it. */
269 2 : TALER_TESTING_cmd_admin_add_incoming ("create-reserve-2",
270 : "EUR:30.04",
271 : &cred.ba,
272 : cred.user42_payto),
273 2 : TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-2",
274 : "EUR:30.04",
275 : cred.user42_payto,
276 : cred.exchange_payto,
277 : "create-reserve-2"),
278 2 : CMD_EXEC_WIREWATCH ("wirewatch-5"),
279 2 : TALER_TESTING_cmd_batch_withdraw ("batch-withdraw-revocation",
280 : "create-reserve-2",
281 : MHD_HTTP_OK,
282 : "EUR:10",
283 : "EUR:10",
284 : "EUR:10",
285 : NULL),
286 2 : TALER_TESTING_cmd_revoke ("revoke-5-EUR:10",
287 : MHD_HTTP_OK,
288 : "batch-withdraw-revocation",
289 : config_file),
290 2 : TALER_TESTING_cmd_recoup_batch ("recoup-batch",
291 : MHD_HTTP_OK,
292 : "batch-withdraw-revocation",
293 : "0,2",
294 : "EUR:10"),
295 2 : TALER_TESTING_cmd_coin_history ("coin-history-recouped",
296 : "batch-withdraw-revocation#0",
297 : "EUR:0",
298 : MHD_HTTP_OK),
299 : /* the coin that was not recouped is still unknown to the exchange */
300 2 : TALER_TESTING_cmd_coin_history ("coin-history-not-recouped",
301 : "batch-withdraw-revocation#1",
302 : "EUR:10",
303 : MHD_HTTP_NOT_FOUND),
304 2 : TALER_TESTING_cmd_reserve_history ("reserve-history-recoup",
305 : "create-reserve-2",
306 : "EUR:20.01",
307 : MHD_HTTP_OK),
308 : /* replaying the request is idempotent (last, as the history
309 : checks above would otherwise expect its entries twice) */
310 2 : TALER_TESTING_cmd_recoup_batch ("recoup-batch-again",
311 : MHD_HTTP_OK,
312 : "batch-withdraw-revocation",
313 : "0,2",
314 : "EUR:10"),
315 : /* Revocation of an online signing key: get a coin to spend after
316 : the revocation (age-restricted, as the plain denominations of
317 : all these values were revoked above) ... */
318 2 : TALER_TESTING_cmd_admin_add_incoming ("create-reserve-3",
319 : "EUR:1.01",
320 : &cred.ba,
321 : cred.user42_payto),
322 2 : CMD_EXEC_WIREWATCH ("wirewatch-6"),
323 2 : TALER_TESTING_cmd_withdraw_amount ("withdraw-signkey-revocation",
324 : "create-reserve-3",
325 : "EUR:1",
326 : 13, /* age restriction */
327 : MHD_HTTP_OK),
328 : /* ... revoke the key that signed "deposit-full", which the exchange
329 : must refuse with a bad master signature ... */
330 2 : TALER_TESTING_cmd_revoke_sign_key ("revoke-signkey-bad-sig",
331 : MHD_HTTP_FORBIDDEN,
332 : true,
333 : "deposit-full"),
334 2 : TALER_TESTING_cmd_revoke_sign_key ("revoke-signkey",
335 : MHD_HTTP_NO_CONTENT,
336 : false,
337 : "deposit-full"),
338 : /* ... sign the replacement key the security module created ... */
339 2 : TALER_TESTING_cmd_exec_offline_sign_keys ("sign-replacement-signkey",
340 : config_file),
341 : /* ... then /keys must no longer offer the revoked key, so that
342 : clients reject replies signed with it ... */
343 2 : TALER_TESTING_cmd_get_exchange ("get-exchange-after-signkey-revocation",
344 2 : cred.cfg,
345 : NULL,
346 : true,
347 : true),
348 2 : TALER_TESTING_cmd_check_signkey_revoked (
349 : "check-signkey-revoked",
350 : "deposit-full",
351 : "get-exchange",
352 : "get-exchange-after-signkey-revocation"),
353 : /* ... and the exchange must sign with the replacement key, which
354 : the client accepts. */
355 2 : TALER_TESTING_cmd_deposit (
356 : "deposit-after-signkey-revocation",
357 : "withdraw-signkey-revocation",
358 : 0,
359 : cred.user42_payto,
360 : "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}",
361 2 : GNUNET_TIME_UNIT_ZERO,
362 : "EUR:1",
363 : MHD_HTTP_OK),
364 2 : TALER_TESTING_cmd_end ()
365 : };
366 :
367 : (void) cls;
368 2 : TALER_TESTING_run (is,
369 : revocation);
370 2 : }
371 :
372 :
373 : int
374 2 : main (int argc,
375 : char *const *argv)
376 : {
377 : (void) argc;
378 : {
379 : char *cipher;
380 :
381 2 : cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
382 2 : GNUNET_assert (NULL != cipher);
383 2 : GNUNET_asprintf (&config_file,
384 : "test_exchange_api-%s.conf",
385 : cipher);
386 2 : GNUNET_free (cipher);
387 : }
388 2 : return TALER_TESTING_main (argv,
389 : "INFO",
390 : config_file,
391 : "exchange-account-2",
392 : TALER_TESTING_BS_FAKEBANK,
393 : &cred,
394 : &run,
395 : NULL);
396 : }
397 :
398 :
399 : /* end of test_exchange_api_revocation.c */
|