test_exchange_api_revocation.c (16942B)
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 CMD_EXEC_WIREWATCH (const char *label) 54 { 55 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 run (void *cls, 69 struct TALER_TESTING_Interpreter *is) 70 { 71 struct TALER_TESTING_Command revocation[] = { 72 TALER_TESTING_cmd_run_fakebank ("run-fakebank", 73 cred.cfg, 74 "exchange-account-2"), 75 TALER_TESTING_cmd_system_start ("start-taler", 76 config_file, 77 "-e", 78 NULL), 79 TALER_TESTING_cmd_get_exchange ("get-exchange", 80 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 TALER_TESTING_cmd_admin_add_incoming ("create-reserve-1", 89 "EUR:10.02", 90 &cred.ba, 91 cred.user42_payto), 92 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 CMD_EXEC_WIREWATCH ("wirewatch-4"), 101 /* Withdraw a 5 EUR coin, at fee of 1 ct */ 102 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 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 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 GNUNET_TIME_UNIT_ZERO, 124 "EUR:1", 125 MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS)), 126 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 CMD_EXEC_WIREWATCH ( 133 "import-kyc-account-withdraw"), 134 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 GNUNET_TIME_UNIT_ZERO, 141 "EUR:1", 142 MHD_HTTP_OK), 143 /* Deposit another coin in full */ 144 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 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 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 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 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 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 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 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 TALER_TESTING_cmd_recoup_refresh ("recoup-1a", 190 MHD_HTTP_OK, 191 "refresh-reveal-1#0", 192 "refresh-melt-1", 193 "EUR:1"), 194 TALER_TESTING_cmd_recoup_refresh ("recoup-1b", 195 MHD_HTTP_OK, 196 "refresh-reveal-1#1", 197 "refresh-melt-1", 198 "EUR:1"), 199 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 TALER_TESTING_cmd_recoup_refresh ("recoup-1c", 206 MHD_HTTP_OK, 207 "refresh-reveal-1#2", 208 "refresh-melt-1", 209 "EUR:1"), 210 TALER_TESTING_cmd_recoup_refresh ("recoup-1c", 211 MHD_HTTP_OK, 212 "refresh-reveal-1#2", 213 "refresh-melt-1", 214 "EUR:1"), 215 TALER_TESTING_cmd_recoup_refresh ("recoup-1c", 216 MHD_HTTP_OK, 217 "refresh-reveal-1#2", 218 "refresh-melt-1", 219 "EUR:1"), 220 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 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 TALER_TESTING_cmd_melt_reveal ("refresh-reveal-2", 238 "refresh-melt-2", 239 MHD_HTTP_OK), 240 /* Revokes refreshed EUR:0.1 coin */ 241 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 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 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 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 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 TALER_TESTING_cmd_admin_add_incoming ("create-reserve-2", 270 "EUR:30.04", 271 &cred.ba, 272 cred.user42_payto), 273 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 CMD_EXEC_WIREWATCH ("wirewatch-5"), 279 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 TALER_TESTING_cmd_revoke ("revoke-5-EUR:10", 287 MHD_HTTP_OK, 288 "batch-withdraw-revocation", 289 config_file), 290 TALER_TESTING_cmd_recoup_batch ("recoup-batch", 291 MHD_HTTP_OK, 292 "batch-withdraw-revocation", 293 "0,2", 294 "EUR:10"), 295 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 TALER_TESTING_cmd_coin_history ("coin-history-not-recouped", 301 "batch-withdraw-revocation#1", 302 "EUR:10", 303 MHD_HTTP_NOT_FOUND), 304 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 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 TALER_TESTING_cmd_admin_add_incoming ("create-reserve-3", 319 "EUR:1.01", 320 &cred.ba, 321 cred.user42_payto), 322 CMD_EXEC_WIREWATCH ("wirewatch-6"), 323 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 TALER_TESTING_cmd_revoke_sign_key ("revoke-signkey-bad-sig", 331 MHD_HTTP_FORBIDDEN, 332 true, 333 "deposit-full"), 334 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 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 TALER_TESTING_cmd_get_exchange ("get-exchange-after-signkey-revocation", 344 cred.cfg, 345 NULL, 346 true, 347 true), 348 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 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 GNUNET_TIME_UNIT_ZERO, 362 "EUR:1", 363 MHD_HTTP_OK), 364 TALER_TESTING_cmd_end () 365 }; 366 367 (void) cls; 368 TALER_TESTING_run (is, 369 revocation); 370 } 371 372 373 int 374 main (int argc, 375 char *const *argv) 376 { 377 (void) argc; 378 { 379 char *cipher; 380 381 cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); 382 GNUNET_assert (NULL != cipher); 383 GNUNET_asprintf (&config_file, 384 "test_exchange_api-%s.conf", 385 cipher); 386 GNUNET_free (cipher); 387 } 388 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 */