diff options
Diffstat (limited to 'src/testing/testing_cmd_challenge_answer.c')
-rw-r--r-- | src/testing/testing_cmd_challenge_answer.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/src/testing/testing_cmd_challenge_answer.c b/src/testing/testing_cmd_challenge_answer.c new file mode 100644 index 0000000..b243d61 --- /dev/null +++ b/src/testing/testing_cmd_challenge_answer.c | |||
@@ -0,0 +1,584 @@ | |||
1 | /* | ||
2 | This file is part of Anastasis | ||
3 | Copyright (C) 2020 Taler Systems SA | ||
4 | |||
5 | Anastasis is free software; you can redistribute it and/or modify it under the | ||
6 | terms of the GNU Lesser General Public License as published by the Free Software | ||
7 | Foundation; either version 3, or (at your option) any later version. | ||
8 | |||
9 | Anastasis 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 | Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> | ||
15 | */ | ||
16 | /** | ||
17 | * @file lib/testing_cmd_challenge_answer.c | ||
18 | * @brief command to execute the anastasis recovery service | ||
19 | * @author Christian Grothoff | ||
20 | * @author Dennis Neufeld | ||
21 | * @author Dominik Meister | ||
22 | */ | ||
23 | |||
24 | #include "platform.h" | ||
25 | #include "anastasis_testing_lib.h" | ||
26 | #include <taler/taler_util.h> | ||
27 | #include <taler/taler_testing_lib.h> | ||
28 | #include <taler/taler_merchant_service.h> | ||
29 | |||
30 | |||
31 | /** | ||
32 | * State for a "challenge answer" CMD. | ||
33 | */ | ||
34 | struct ChallengeState | ||
35 | { | ||
36 | /** | ||
37 | * The interpreter state. | ||
38 | */ | ||
39 | struct TALER_TESTING_Interpreter *is; | ||
40 | |||
41 | /** | ||
42 | * Reference to the challenge we are solving | ||
43 | */ | ||
44 | struct ANASTASIS_Challenge *c; | ||
45 | |||
46 | /** | ||
47 | * Answer to the challenge we are solving | ||
48 | */ | ||
49 | const char *answer; | ||
50 | |||
51 | /** | ||
52 | * Reference to the recovery process | ||
53 | */ | ||
54 | const char *challenge_ref; | ||
55 | |||
56 | /** | ||
57 | * Reference to the payment | ||
58 | */ | ||
59 | const char *payment_ref; | ||
60 | |||
61 | /** | ||
62 | * "taler://pay/" URL we got back, if any. Otherwise NULL. | ||
63 | */ | ||
64 | char *payment_uri; | ||
65 | |||
66 | /** | ||
67 | * Order ID extracted from @e payment_uri, or NULL. | ||
68 | */ | ||
69 | char *order_id; | ||
70 | |||
71 | /** | ||
72 | * Payment order ID we are to provide in the request. | ||
73 | */ | ||
74 | struct ANASTASIS_PaymentSecretP payment_order_req; | ||
75 | |||
76 | /** | ||
77 | * Expected status code. | ||
78 | */ | ||
79 | enum ANASTASIS_ChallengeStatus expected_cs; | ||
80 | |||
81 | /** | ||
82 | * Index of the challenge we are solving | ||
83 | */ | ||
84 | unsigned int challenge_index; | ||
85 | |||
86 | /** | ||
87 | * 0 for no plugin needed 1 for plugin needed to authenticate | ||
88 | */ | ||
89 | unsigned int mode; | ||
90 | |||
91 | /** | ||
92 | * code we read in the file generated by the plugin | ||
93 | */ | ||
94 | char code[22]; | ||
95 | |||
96 | }; | ||
97 | |||
98 | |||
99 | static void | ||
100 | challenge_answer_cb (void *af_cls, | ||
101 | const struct ANASTASIS_ChallengeStartResponse *csr) | ||
102 | { | ||
103 | struct ChallengeState *cs = af_cls; | ||
104 | |||
105 | cs->c = NULL; | ||
106 | if (csr->cs != cs->expected_cs) | ||
107 | { | ||
108 | GNUNET_break (0); | ||
109 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
110 | "Expected status %u, got %u\n", | ||
111 | cs->expected_cs, | ||
112 | csr->cs); | ||
113 | TALER_TESTING_interpreter_fail (cs->is); | ||
114 | return; | ||
115 | } | ||
116 | switch (csr->cs) | ||
117 | { | ||
118 | case ANASTASIS_CHALLENGE_STATUS_SOLVED: | ||
119 | break; | ||
120 | case ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS: | ||
121 | { | ||
122 | FILE *file; | ||
123 | char *fn; | ||
124 | |||
125 | if (0 == strcasecmp (csr->details.open_challenge.content_type, | ||
126 | "application/json")) | ||
127 | { | ||
128 | const char *filename; | ||
129 | json_t *in; | ||
130 | |||
131 | in = json_loadb (csr->details.open_challenge.body, | ||
132 | csr->details.open_challenge.body_size, | ||
133 | JSON_REJECT_DUPLICATES, | ||
134 | NULL); | ||
135 | if (NULL == in) | ||
136 | { | ||
137 | GNUNET_break (0); | ||
138 | TALER_TESTING_interpreter_fail (cs->is); | ||
139 | return; | ||
140 | } | ||
141 | filename = json_string_value (json_object_get (in, | ||
142 | "filename")); | ||
143 | if (NULL == filename) | ||
144 | { | ||
145 | GNUNET_break (0); | ||
146 | json_decref (in); | ||
147 | TALER_TESTING_interpreter_fail (cs->is); | ||
148 | return; | ||
149 | } | ||
150 | fn = GNUNET_strdup (filename); | ||
151 | json_decref (in); | ||
152 | } | ||
153 | else | ||
154 | { | ||
155 | fn = GNUNET_strndup (csr->details.open_challenge.body, | ||
156 | csr->details.open_challenge.body_size); | ||
157 | } | ||
158 | file = fopen (fn, | ||
159 | "r"); | ||
160 | if (NULL == file) | ||
161 | { | ||
162 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
163 | "open", | ||
164 | fn); | ||
165 | GNUNET_free (fn); | ||
166 | TALER_TESTING_interpreter_fail (cs->is); | ||
167 | return; | ||
168 | } | ||
169 | if (0 == fscanf (file, | ||
170 | "%21s", | ||
171 | cs->code)) | ||
172 | { | ||
173 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
174 | "fscanf", | ||
175 | fn); | ||
176 | TALER_TESTING_interpreter_fail (cs->is); | ||
177 | fclose (file); | ||
178 | GNUNET_free (fn); | ||
179 | return; | ||
180 | } | ||
181 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
182 | "Read challenge answer `%s' from file `%s'\n", | ||
183 | cs->code, | ||
184 | fn); | ||
185 | TALER_TESTING_interpreter_next (cs->is); | ||
186 | GNUNET_break (0 == fclose (file)); | ||
187 | GNUNET_free (fn); | ||
188 | return; | ||
189 | } | ||
190 | case ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED: | ||
191 | if (0 != strncmp (csr->details.payment_required.taler_pay_uri, | ||
192 | "taler+http://pay/", | ||
193 | strlen ("taler+http://pay/"))) | ||
194 | { | ||
195 | GNUNET_break (0); | ||
196 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
197 | "Invalid payment URI `%s'\n", | ||
198 | csr->details.payment_required.taler_pay_uri); | ||
199 | TALER_TESTING_interpreter_fail (cs->is); | ||
200 | return; | ||
201 | } | ||
202 | cs->payment_uri = GNUNET_strdup ( | ||
203 | csr->details.payment_required.taler_pay_uri); | ||
204 | { | ||
205 | struct TALER_MERCHANT_PayUriData pud; | ||
206 | |||
207 | if (GNUNET_OK != | ||
208 | TALER_MERCHANT_parse_pay_uri (cs->payment_uri, | ||
209 | &pud)) | ||
210 | { | ||
211 | GNUNET_break (0); | ||
212 | TALER_TESTING_interpreter_fail (cs->is); | ||
213 | return; | ||
214 | } | ||
215 | cs->order_id = GNUNET_strdup (pud.order_id); | ||
216 | if (GNUNET_OK != | ||
217 | GNUNET_STRINGS_string_to_data (cs->order_id, | ||
218 | strlen (cs->order_id), | ||
219 | &cs->payment_order_req, | ||
220 | sizeof (cs->payment_order_req))) | ||
221 | { | ||
222 | GNUNET_break (0); | ||
223 | TALER_TESTING_interpreter_fail (cs->is); | ||
224 | return; | ||
225 | } | ||
226 | TALER_MERCHANT_parse_pay_uri_free (&pud); | ||
227 | } | ||
228 | TALER_TESTING_interpreter_next (cs->is); | ||
229 | return; | ||
230 | case ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN: | ||
231 | break; | ||
232 | case ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION: | ||
233 | break; | ||
234 | case ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE: | ||
235 | GNUNET_break (0); | ||
236 | TALER_TESTING_interpreter_fail (cs->is); | ||
237 | return; | ||
238 | case ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED: | ||
239 | break; | ||
240 | } | ||
241 | TALER_TESTING_interpreter_next (cs->is); | ||
242 | } | ||
243 | |||
244 | |||
245 | /** | ||
246 | * Run a "recover secret" CMD. | ||
247 | * | ||
248 | * @param cls closure. | ||
249 | * @param cmd command currently being run. | ||
250 | * @param is interpreter state. | ||
251 | */ | ||
252 | static void | ||
253 | challenge_answer_run (void *cls, | ||
254 | const struct TALER_TESTING_Command *cmd, | ||
255 | struct TALER_TESTING_Interpreter *is) | ||
256 | { | ||
257 | struct ChallengeState *cs = cls; | ||
258 | const struct ANASTASIS_Challenge *c; | ||
259 | const struct ANASTASIS_PaymentSecretP *ps; | ||
260 | |||
261 | cs->is = is; | ||
262 | if (NULL != cs->challenge_ref) | ||
263 | { | ||
264 | const struct TALER_TESTING_Command *ref; | ||
265 | |||
266 | ref = TALER_TESTING_interpreter_lookup_command ( | ||
267 | is, | ||
268 | cs->challenge_ref); | ||
269 | if (NULL == ref) | ||
270 | { | ||
271 | GNUNET_break (0); | ||
272 | TALER_TESTING_interpreter_fail (cs->is); | ||
273 | return; | ||
274 | } | ||
275 | if (GNUNET_OK != | ||
276 | ANASTASIS_TESTING_get_trait_challenge (ref, | ||
277 | cs->challenge_index, | ||
278 | &c)) | ||
279 | { | ||
280 | GNUNET_break (0); | ||
281 | TALER_TESTING_interpreter_fail (cs->is); | ||
282 | return; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (NULL != cs->payment_ref) | ||
287 | { | ||
288 | const struct TALER_TESTING_Command *ref; | ||
289 | |||
290 | ref = TALER_TESTING_interpreter_lookup_command (is, | ||
291 | cs->payment_ref); | ||
292 | if (NULL == ref) | ||
293 | { | ||
294 | GNUNET_break (0); | ||
295 | TALER_TESTING_interpreter_fail (cs->is); | ||
296 | return; | ||
297 | } | ||
298 | if (GNUNET_OK != | ||
299 | ANASTASIS_TESTING_get_trait_payment_secret (ref, | ||
300 | 0, | ||
301 | &ps)) | ||
302 | { | ||
303 | GNUNET_break (0); | ||
304 | TALER_TESTING_interpreter_fail (cs->is); | ||
305 | return; | ||
306 | } | ||
307 | } | ||
308 | else | ||
309 | { | ||
310 | ps = NULL; | ||
311 | } | ||
312 | |||
313 | cs->c = (struct ANASTASIS_Challenge *) c; | ||
314 | |||
315 | if (1 == cs->mode) | ||
316 | { | ||
317 | const struct TALER_TESTING_Command *ref; | ||
318 | const char *answer; | ||
319 | unsigned long long code; | ||
320 | char dummy; | ||
321 | |||
322 | ref = TALER_TESTING_interpreter_lookup_command (is, | ||
323 | cs->answer); | ||
324 | if (NULL == ref) | ||
325 | { | ||
326 | GNUNET_break (0); | ||
327 | TALER_TESTING_interpreter_fail (cs->is); | ||
328 | return; | ||
329 | } | ||
330 | if (GNUNET_OK != | ||
331 | ANASTASIS_TESTING_get_trait_code (ref, | ||
332 | 0, | ||
333 | &answer)) | ||
334 | { | ||
335 | GNUNET_break (0); | ||
336 | TALER_TESTING_interpreter_fail (cs->is); | ||
337 | return; | ||
338 | } | ||
339 | if (1 != | ||
340 | sscanf (answer, | ||
341 | "%llu%c", | ||
342 | &code, | ||
343 | &dummy)) | ||
344 | { | ||
345 | GNUNET_break (0); | ||
346 | TALER_TESTING_interpreter_fail (cs->is); | ||
347 | return; | ||
348 | } | ||
349 | if (GNUNET_OK != | ||
350 | ANASTASIS_challenge_answer2 (cs->c, | ||
351 | ps, | ||
352 | GNUNET_TIME_UNIT_ZERO, | ||
353 | code, | ||
354 | &challenge_answer_cb, | ||
355 | cs)) | ||
356 | { | ||
357 | GNUNET_break (0); | ||
358 | cs->c = NULL; | ||
359 | TALER_TESTING_interpreter_fail (cs->is); | ||
360 | return; | ||
361 | } | ||
362 | |||
363 | } | ||
364 | else | ||
365 | { | ||
366 | if (GNUNET_OK != | ||
367 | ANASTASIS_challenge_answer (cs->c, | ||
368 | ps, | ||
369 | GNUNET_TIME_UNIT_ZERO, | ||
370 | cs->answer, | ||
371 | &challenge_answer_cb, | ||
372 | cs)) | ||
373 | { | ||
374 | GNUNET_break (0); | ||
375 | cs->c = NULL; | ||
376 | TALER_TESTING_interpreter_fail (cs->is); | ||
377 | return; | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | |||
382 | |||
383 | /** | ||
384 | * Run a "recover secret" CMD. | ||
385 | * | ||
386 | * @param cls closure. | ||
387 | * @param cmd command currently being run. | ||
388 | * @param is interpreter state. | ||
389 | */ | ||
390 | static void | ||
391 | challenge_start_run (void *cls, | ||
392 | const struct TALER_TESTING_Command *cmd, | ||
393 | struct TALER_TESTING_Interpreter *is) | ||
394 | { | ||
395 | struct ChallengeState *cs = cls; | ||
396 | const struct ANASTASIS_Challenge *c; | ||
397 | const struct TALER_TESTING_Command *ref; | ||
398 | const struct ANASTASIS_PaymentSecretP *ps; | ||
399 | |||
400 | cs->is = is; | ||
401 | ref = TALER_TESTING_interpreter_lookup_command ( | ||
402 | is, | ||
403 | cs->challenge_ref); | ||
404 | if (NULL == ref) | ||
405 | { | ||
406 | GNUNET_break (0); | ||
407 | TALER_TESTING_interpreter_fail (cs->is); | ||
408 | return; | ||
409 | } | ||
410 | if (GNUNET_OK != | ||
411 | ANASTASIS_TESTING_get_trait_challenge (ref, | ||
412 | cs->challenge_index, | ||
413 | &c)) | ||
414 | { | ||
415 | GNUNET_break (0); | ||
416 | TALER_TESTING_interpreter_fail (cs->is); | ||
417 | return; | ||
418 | } | ||
419 | if (NULL != cs->payment_ref) | ||
420 | { | ||
421 | const struct TALER_TESTING_Command *ref; | ||
422 | |||
423 | ref = TALER_TESTING_interpreter_lookup_command (is, | ||
424 | cs->payment_ref); | ||
425 | if (NULL == ref) | ||
426 | { | ||
427 | GNUNET_break (0); | ||
428 | TALER_TESTING_interpreter_fail (cs->is); | ||
429 | return; | ||
430 | } | ||
431 | if (GNUNET_OK != | ||
432 | ANASTASIS_TESTING_get_trait_payment_secret (ref, | ||
433 | 0, | ||
434 | &ps)) | ||
435 | { | ||
436 | GNUNET_break (0); | ||
437 | TALER_TESTING_interpreter_fail (cs->is); | ||
438 | return; | ||
439 | } | ||
440 | } | ||
441 | else | ||
442 | { | ||
443 | ps = NULL; | ||
444 | } | ||
445 | if (GNUNET_OK != | ||
446 | ANASTASIS_challenge_start ((struct ANASTASIS_Challenge *) c, | ||
447 | ps, | ||
448 | GNUNET_TIME_UNIT_ZERO, | ||
449 | NULL, | ||
450 | &challenge_answer_cb, | ||
451 | cs)) | ||
452 | { | ||
453 | GNUNET_break (0); | ||
454 | TALER_TESTING_interpreter_fail (cs->is); | ||
455 | return; | ||
456 | } | ||
457 | } | ||
458 | |||
459 | |||
460 | /** | ||
461 | * Free the state of a "recover secret" CMD, and possibly | ||
462 | * cancel it if it did not complete. | ||
463 | * | ||
464 | * @param cls closure. | ||
465 | * @param cmd command being freed. | ||
466 | */ | ||
467 | static void | ||
468 | challenge_cleanup (void *cls, | ||
469 | const struct TALER_TESTING_Command *cmd) | ||
470 | { | ||
471 | struct ChallengeState *cs = cls; | ||
472 | |||
473 | if (NULL != cs->c) | ||
474 | { | ||
475 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
476 | "Command '%s' did not complete (challenge answer)\n", | ||
477 | cmd->label); | ||
478 | ANASTASIS_challenge_abort (cs->c); | ||
479 | cs->c = NULL; | ||
480 | } | ||
481 | GNUNET_free (cs->payment_uri); | ||
482 | GNUNET_free (cs->order_id); | ||
483 | GNUNET_free (cs); | ||
484 | } | ||
485 | |||
486 | |||
487 | /** | ||
488 | * Offer internal data to other commands. | ||
489 | * | ||
490 | * @param cls closure | ||
491 | * @param ret[out] result (could be anything) | ||
492 | * @param trait name of the trait | ||
493 | * @param index index number of the object to extract. | ||
494 | * @return #GNUNET_OK on success | ||
495 | */ | ||
496 | static int | ||
497 | challenge_create_traits (void *cls, | ||
498 | const void **ret, | ||
499 | const char *trait, | ||
500 | unsigned int index) | ||
501 | { | ||
502 | struct ChallengeState *cs = cls; | ||
503 | struct TALER_TESTING_Trait traits[] = { | ||
504 | ANASTASIS_TESTING_make_trait_code (0, | ||
505 | cs->code), | ||
506 | ANASTASIS_TESTING_make_trait_payment_secret (0, | ||
507 | &cs->payment_order_req), | ||
508 | TALER_TESTING_make_trait_url (TALER_TESTING_UT_TALER_URL, | ||
509 | cs->payment_uri), | ||
510 | TALER_TESTING_make_trait_order_id (0, | ||
511 | cs->order_id), | ||
512 | TALER_TESTING_trait_end () | ||
513 | }; | ||
514 | |||
515 | return TALER_TESTING_get_trait (traits, | ||
516 | ret, | ||
517 | trait, | ||
518 | index); | ||
519 | } | ||
520 | |||
521 | |||
522 | struct TALER_TESTING_Command | ||
523 | ANASTASIS_TESTING_cmd_challenge_start ( | ||
524 | const char *label, | ||
525 | const char *payment_ref, | ||
526 | const char *challenge_ref, | ||
527 | unsigned int challenge_index, | ||
528 | enum ANASTASIS_ChallengeStatus expected_cs) | ||
529 | { | ||
530 | struct ChallengeState *cs; | ||
531 | |||
532 | cs = GNUNET_new (struct ChallengeState); | ||
533 | cs->expected_cs = expected_cs; | ||
534 | cs->challenge_ref = challenge_ref; | ||
535 | cs->payment_ref = payment_ref; | ||
536 | cs->challenge_index = challenge_index; | ||
537 | { | ||
538 | struct TALER_TESTING_Command cmd = { | ||
539 | .cls = cs, | ||
540 | .label = label, | ||
541 | .run = &challenge_start_run, | ||
542 | .cleanup = &challenge_cleanup, | ||
543 | .traits = &challenge_create_traits | ||
544 | }; | ||
545 | |||
546 | return cmd; | ||
547 | } | ||
548 | } | ||
549 | |||
550 | |||
551 | struct TALER_TESTING_Command | ||
552 | ANASTASIS_TESTING_cmd_challenge_answer ( | ||
553 | const char *label, | ||
554 | const char *payment_ref, | ||
555 | const char *challenge_ref, | ||
556 | unsigned int challenge_index, | ||
557 | const char *answer, | ||
558 | unsigned int mode, | ||
559 | enum ANASTASIS_ChallengeStatus expected_cs) | ||
560 | { | ||
561 | struct ChallengeState *cs; | ||
562 | |||
563 | cs = GNUNET_new (struct ChallengeState); | ||
564 | cs->expected_cs = expected_cs; | ||
565 | cs->challenge_ref = challenge_ref; | ||
566 | cs->payment_ref = payment_ref; | ||
567 | cs->answer = answer; | ||
568 | cs->challenge_index = challenge_index; | ||
569 | cs->mode = mode; | ||
570 | { | ||
571 | struct TALER_TESTING_Command cmd = { | ||
572 | .cls = cs, | ||
573 | .label = label, | ||
574 | .run = &challenge_answer_run, | ||
575 | .cleanup = &challenge_cleanup, | ||
576 | .traits = &challenge_create_traits | ||
577 | }; | ||
578 | |||
579 | return cmd; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | |||
584 | /* end of testing_cmd_challenge_answer.c */ | ||