/*
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_TAN_ALREADY_SENT:
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 */