/* This file is part of Anastasis Copyright (C) 2020, 2021, 2022 Anastasis SARL Anastasis is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Anastasis; see the file COPYING.GPL. If not, see */ /** * @file testing/testing_cmd_challenge_answer.c * @brief command to execute the anastasis recovery service * @author Christian Grothoff * @author Dennis Neufeld * @author Dominik Meister */ #include "platform.h" #include "anastasis_testing_lib.h" #include #include #include // FIXME: break up into two files, one for start, one for answer! /** * State for a "challenge answer" CMD. */ struct ChallengeState { /** * The interpreter state. */ struct TALER_TESTING_Interpreter *is; /** * Reference to the challenge we are solving */ struct ANASTASIS_Challenge *c; /** * Answer to the challenge we are solving */ const char *answer; /** * Reference to the recovery process */ const char *challenge_ref; /** * Reference to the payment */ const char *payment_ref; /** * "taler://pay/" URL we got back, if any. Otherwise NULL. */ char *payment_uri; /** * Order ID extracted from @e payment_uri, or NULL. */ char *order_id; /** * Payment order ID we are to provide in the request. */ struct ANASTASIS_PaymentSecretP payment_order_req; /** * Expected answer status code. */ enum ANASTASIS_ChallengeAnswerStatus expected_acs; /** * Expected start status code. */ enum ANASTASIS_ChallengeStartStatus expected_scs; /** * Index of the challenge we are solving */ unsigned int challenge_index; /** * 0 for no plugin needed 1 for plugin needed to authenticate */ unsigned int mode; /** * code we read in the file generated by the plugin */ char *code; }; static void challenge_answer_cb (void *af_cls, const struct ANASTASIS_ChallengeAnswerResponse *csr) { struct ChallengeState *cs = af_cls; cs->c = NULL; if (csr->cs != cs->expected_acs) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected status %u, got %u\n", cs->expected_acs, csr->cs); TALER_TESTING_interpreter_fail (cs->is); return; } switch (csr->cs) { case ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED: break; case ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER: break; case ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED: if (0 != strncmp (csr->details.payment_required.taler_pay_uri, "taler+http://pay/", strlen ("taler+http://pay/"))) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid payment URI `%s'\n", csr->details.payment_required.taler_pay_uri); TALER_TESTING_interpreter_fail (cs->is); return; } cs->payment_uri = GNUNET_strdup ( csr->details.payment_required.taler_pay_uri); { struct TALER_MERCHANT_PayUriData pud; if (GNUNET_OK != TALER_MERCHANT_parse_pay_uri (cs->payment_uri, &pud)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } cs->order_id = GNUNET_strdup (pud.order_id); if (GNUNET_OK != GNUNET_STRINGS_string_to_data (cs->order_id, strlen (cs->order_id), &cs->payment_order_req, sizeof (cs->payment_order_req))) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } TALER_MERCHANT_parse_pay_uri_free (&pud); } TALER_TESTING_interpreter_next (cs->is); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN: break; case ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE: GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; case ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED: break; case ANASTASIS_CHALLENGE_ANSWER_STATUS_AUTH_TIMEOUT: break; } TALER_TESTING_interpreter_next (cs->is); } /** * Run a "recover secret" CMD. * * @param cls closure. * @param cmd command currently being run. * @param is interpreter state. */ static void challenge_answer_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { struct ChallengeState *cs = cls; const struct ANASTASIS_Challenge **c; const struct ANASTASIS_PaymentSecretP *ps; cs->is = is; if (NULL != cs->challenge_ref) { const struct TALER_TESTING_Command *ref; ref = TALER_TESTING_interpreter_lookup_command ( is, cs->challenge_ref); if (NULL == ref) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (GNUNET_OK != ANASTASIS_TESTING_get_trait_challenges (ref, cs->challenge_index, &c)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } cs->c = (struct ANASTASIS_Challenge *) *c; } if (NULL != cs->payment_ref) { const struct TALER_TESTING_Command *ref; ref = TALER_TESTING_interpreter_lookup_command (is, cs->payment_ref); if (NULL == ref) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (GNUNET_OK != ANASTASIS_TESTING_get_trait_payment_secret (ref, &ps)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } } else { ps = NULL; } if (1 == cs->mode) { const struct TALER_TESTING_Command *ref; const char **answer; unsigned long long code; char dummy; ref = TALER_TESTING_interpreter_lookup_command (is, cs->answer); if (NULL == ref) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (GNUNET_OK != ANASTASIS_TESTING_get_trait_code (ref, &answer)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (1 != sscanf (*answer, "%llu%c", &code, &dummy)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (GNUNET_OK != ANASTASIS_challenge_answer2 (cs->c, ps, GNUNET_TIME_UNIT_ZERO, code, &challenge_answer_cb, cs)) { GNUNET_break (0); cs->c = NULL; TALER_TESTING_interpreter_fail (cs->is); return; } } else { if (GNUNET_OK != ANASTASIS_challenge_answer (cs->c, ps, GNUNET_TIME_UNIT_ZERO, cs->answer, &challenge_answer_cb, cs)) { GNUNET_break (0); cs->c = NULL; TALER_TESTING_interpreter_fail (cs->is); return; } } } static void challenge_start_cb (void *af_cls, const struct ANASTASIS_ChallengeStartResponse *csr) { struct ChallengeState *cs = af_cls; cs->c = NULL; if (csr->cs != cs->expected_scs) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected status %u, got %u\n", cs->expected_scs, csr->cs); TALER_TESTING_interpreter_fail (cs->is); return; } switch (csr->cs) { case ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED: { FILE *file; char code[22]; file = fopen (csr->details.tan_filename, "r"); if (NULL == file) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", csr->details.tan_filename); TALER_TESTING_interpreter_fail (cs->is); return; } if (0 == fscanf (file, "%21s", code)) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fscanf", csr->details.tan_filename); GNUNET_break (0 == fclose (file)); TALER_TESTING_interpreter_fail (cs->is); return; } GNUNET_break (0 == fclose (file)); cs->code = GNUNET_strdup (code); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read code `%s'\n", code); } break; case ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED: GNUNET_break (0); /* FIXME: not implemented */ break; case ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED: GNUNET_break (0); /* FIXME: not implemented */ break; case ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED: if (0 != strncmp (csr->details.payment_required.taler_pay_uri, "taler+http://pay/", strlen ("taler+http://pay/"))) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid payment URI `%s'\n", csr->details.payment_required.taler_pay_uri); TALER_TESTING_interpreter_fail (cs->is); return; } cs->payment_uri = GNUNET_strdup ( csr->details.payment_required.taler_pay_uri); { struct TALER_MERCHANT_PayUriData pud; if (GNUNET_OK != TALER_MERCHANT_parse_pay_uri (cs->payment_uri, &pud)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } cs->order_id = GNUNET_strdup (pud.order_id); if (GNUNET_OK != GNUNET_STRINGS_string_to_data (cs->order_id, strlen (cs->order_id), &cs->payment_order_req, sizeof (cs->payment_order_req))) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } TALER_MERCHANT_parse_pay_uri_free (&pud); } TALER_TESTING_interpreter_next (cs->is); return; case ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN: break; case ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE: GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } TALER_TESTING_interpreter_next (cs->is); } /** * Run a "recover secret" CMD. * * @param cls closure. * @param cmd command currently being run. * @param is interpreter state. */ static void challenge_start_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { struct ChallengeState *cs = cls; const struct ANASTASIS_Challenge **c; const struct TALER_TESTING_Command *ref; const struct ANASTASIS_PaymentSecretP *ps; cs->is = is; ref = TALER_TESTING_interpreter_lookup_command ( is, cs->challenge_ref); if (NULL == ref) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (GNUNET_OK != ANASTASIS_TESTING_get_trait_challenges (ref, cs->challenge_index, &c)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (NULL != cs->payment_ref) { const struct TALER_TESTING_Command *ref; ref = TALER_TESTING_interpreter_lookup_command (is, cs->payment_ref); if (NULL == ref) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } if (GNUNET_OK != ANASTASIS_TESTING_get_trait_payment_secret (ref, &ps)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } } else { ps = NULL; } if (GNUNET_OK != ANASTASIS_challenge_start ((struct ANASTASIS_Challenge *) *c, ps, &challenge_start_cb, cs)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (cs->is); return; } } /** * Free the state of a "recover secret" CMD, and possibly * cancel it if it did not complete. * * @param cls closure. * @param cmd command being freed. */ static void challenge_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { struct ChallengeState *cs = cls; if (NULL != cs->c) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Command '%s' did not complete (challenge answer)\n", cmd->label); ANASTASIS_challenge_abort (cs->c); cs->c = NULL; } GNUNET_free (cs->payment_uri); GNUNET_free (cs->order_id); GNUNET_free (cs->code); GNUNET_free (cs); } /** * Offer internal data to other commands. * * @param cls closure * @param[out] ret result (could be anything) * @param trait name of the trait * @param index index number of the object to extract. * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue challenge_create_traits (void *cls, const void **ret, const char *trait, unsigned int index) { struct ChallengeState *cs = cls; struct TALER_TESTING_Trait traits[] = { ANASTASIS_TESTING_make_trait_code ( (const char **) &cs->code), ANASTASIS_TESTING_make_trait_payment_secret ( &cs->payment_order_req), TALER_TESTING_make_trait_payto_uri ( (const char **) cs->payment_uri), TALER_TESTING_make_trait_order_id ( (const char **) &cs->order_id), TALER_TESTING_trait_end () }; return TALER_TESTING_get_trait (traits, ret, trait, index); } struct TALER_TESTING_Command ANASTASIS_TESTING_cmd_challenge_start ( const char *label, const char *payment_ref, const char *challenge_ref, unsigned int challenge_index, enum ANASTASIS_ChallengeStartStatus expected_cs) { struct ChallengeState *cs; cs = GNUNET_new (struct ChallengeState); cs->expected_scs = expected_cs; cs->challenge_ref = challenge_ref; cs->payment_ref = payment_ref; cs->challenge_index = challenge_index; { struct TALER_TESTING_Command cmd = { .cls = cs, .label = label, .run = &challenge_start_run, .cleanup = &challenge_cleanup, .traits = &challenge_create_traits }; return cmd; } } struct TALER_TESTING_Command ANASTASIS_TESTING_cmd_challenge_answer ( const char *label, const char *payment_ref, const char *challenge_ref, unsigned int challenge_index, const char *answer, unsigned int mode, enum ANASTASIS_ChallengeAnswerStatus expected_cs) { struct ChallengeState *cs; cs = GNUNET_new (struct ChallengeState); cs->expected_acs = expected_cs; cs->challenge_ref = challenge_ref; cs->payment_ref = payment_ref; cs->answer = answer; cs->challenge_index = challenge_index; cs->mode = mode; { struct TALER_TESTING_Command cmd = { .cls = cs, .label = label, .run = &challenge_answer_run, .cleanup = &challenge_cleanup, .traits = &challenge_create_traits }; return cmd; } } /* end of testing_cmd_challenge_answer.c */