[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-anastasis] branch master updated: clean up state machine in redux
From: |
gnunet |
Subject: |
[taler-anastasis] branch master updated: clean up state machine in redux |
Date: |
Sat, 06 Mar 2021 22:54:56 +0100 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository anastasis.
The following commit(s) were added to refs/heads/master by this push:
new f445071 clean up state machine in redux
f445071 is described below
commit f445071568a63e489ee7098f5512b1662a32d7c5
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Mar 6 22:54:54 2021 +0100
clean up state machine in redux
---
...tasis_reducer_recovery_enter_user_attributes.sh | 11 +-
src/lib/anastasis_recovery.c | 12 +-
src/reducer/anastasis_api_recovery_redux.c | 511 +++++++++++++++++++--
3 files changed, 502 insertions(+), 32 deletions(-)
diff --git a/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh
b/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh
index 1419048..dcd9098 100755
--- a/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh
+++ b/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh
@@ -397,7 +397,7 @@ echo -n "Running challenge logic ..."
UUID0=`jq -r -e .recovery_information.challenges[0].uuid < $R2FILE`
UUID1=`jq -r -e .recovery_information.challenges[1].uuid < $R2FILE`
-valgrind anastasis-reducer -a \
+anastasis-reducer -a \
"$(jq -n '
{
uuid: $UUID
@@ -422,6 +422,15 @@ anastasis-reducer -a \
)" \
solve_challenge < $R1FILE > $R2FILE
+anastasis-reducer -a \
+ "$(jq -n '
+ {
+ uuid: $UUID
+ }' \
+ --arg UUID "$UUID1"
+ )" \
+ select_challenge < $R2FILE > $R1FILE
+
anastasis-reducer -a \
"$(jq -n '
{
diff --git a/src/lib/anastasis_recovery.c b/src/lib/anastasis_recovery.c
index d90c24f..7b3fdf9 100644
--- a/src/lib/anastasis_recovery.c
+++ b/src/lib/anastasis_recovery.c
@@ -1030,9 +1030,15 @@ parse_cs_array (struct ANASTASIS_Recovery *r,
c->ci.type = c->type;
c->instructions = GNUNET_strdup (instructions);
c->ci.instructions = c->instructions;
- if (NULL != json_object_get (cs,
- "key_share"))
- c->ci.solved = true;
+ {
+ json_t *ks;
+
+ ks = json_object_get (cs,
+ "key_share");
+ if ( (NULL != ks) &&
+ (! json_is_null (ks)) )
+ c->ci.solved = true;
+ }
}
return GNUNET_OK;
diff --git a/src/reducer/anastasis_api_recovery_redux.c
b/src/reducer/anastasis_api_recovery_redux.c
index 2872506..bc24be4 100644
--- a/src/reducer/anastasis_api_recovery_redux.c
+++ b/src/reducer/anastasis_api_recovery_redux.c
@@ -141,6 +141,11 @@ struct SelectChallengeContext
* recovered.
*/
struct GNUNET_SCHEDULER_Task *delayed_report;
+
+ /**
+ * Payment secret, if we are in the "pay" state.
+ */
+ struct ANASTASIS_PaymentSecretP ps;
};
@@ -519,6 +524,7 @@ run_challenge_cb (void *cls,
&ps),
GNUNET_JSON_spec_end ()
};
+ json_t *challenge;
if (NULL == ri)
{
@@ -532,42 +538,38 @@ run_challenge_cb (void *cls,
}
/* Check if we got a payment_secret */
+ challenge = find_challenge_in_ri (sctx->state,
+ &sctx->uuid);
+ if (NULL == challenge)
{
- json_t *challenge = find_challenge_in_ri (sctx->state,
- &sctx->uuid);
-
- if (NULL == challenge)
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "challenge not found");
+ sctx_free (sctx);
+ return;
+ }
+ if (NULL !=
+ json_object_get (challenge,
+ "payment_secret"))
+ {
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (sctx->args,
+ spec,
+ NULL, NULL))
{
GNUNET_break_op (0);
ANASTASIS_redux_fail_ (sctx->cb,
sctx->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "challenge not found");
+ "'payment_secret' malformed");
sctx_free (sctx);
return;
}
- if (NULL !=
- json_object_get (challenge,
- "payment_secret"))
- {
- if (GNUNET_OK !=
- GNUNET_JSON_parse (sctx->args,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- ANASTASIS_redux_fail_ (sctx->cb,
- sctx->cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'payment_secret' malformed");
- sctx_free (sctx);
- return;
- }
- psp = &ps;
- }
+ psp = &ps;
}
-
for (unsigned int i = 0; i<ri->cs_len; i++)
{
struct ANASTASIS_Challenge *ci = ri->cs[i];
@@ -575,6 +577,15 @@ run_challenge_cb (void *cls,
int ret;
cd = ANASTASIS_challenge_get_details (ci);
+ if (cd->solved)
+ {
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "Selected challenge already solved");
+ sctx_free (sctx);
+ return;
+ }
if (0 ==
GNUNET_memcmp (&sctx->uuid,
&cd->uuid))
@@ -596,6 +607,11 @@ run_challenge_cb (void *cls,
sctx_free (sctx);
return;
}
+ /* persist answer, in case payment is required */
+ GNUNET_assert (0 ==
+ json_object_set (challenge,
+ "answer",
+ janswer));
ret = ANASTASIS_challenge_answer (ci,
psp,
answer,
@@ -676,6 +692,126 @@ run_challenge_cb (void *cls,
}
+/**
+ * Callback which passes back the recovery document and its possible
+ * policies. Also passes back the version of the document for the user
+ * to check.
+ *
+ * We find the selected challenge and try to answer it (or begin
+ * the process).
+ *
+ * @param cls a `struct SelectChallengeContext *`
+ * @param ri recovery information struct which contains the policies
+ */
+static void
+pay_challenge_cb (void *cls,
+ const struct ANASTASIS_RecoveryInformation *ri)
+{
+ struct SelectChallengeContext *sctx = cls;
+ json_t *challenge;
+
+ if (NULL == ri)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "recovery information could not be deserialized");
+ sctx_free (sctx);
+ return;
+ }
+
+ challenge = find_challenge_in_ri (sctx->state,
+ &sctx->uuid);
+ if (NULL == challenge)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "challenge not found");
+ sctx_free (sctx);
+ return;
+ }
+ /* persist payment, in case we need to run the request again */
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (challenge,
+ "payment_secret",
+ GNUNET_JSON_from_data_auto (&sctx->ps)));
+
+ for (unsigned int i = 0; i<ri->cs_len; i++)
+ {
+ struct ANASTASIS_Challenge *ci = ri->cs[i];
+ const struct ANASTASIS_ChallengeDetails *cd;
+ int ret;
+
+ cd = ANASTASIS_challenge_get_details (ci);
+ if (cd->solved)
+ {
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "Selected challenge already solved");
+ sctx_free (sctx);
+ return;
+ }
+
+ if (0 ==
+ GNUNET_memcmp (&sctx->uuid,
+ &cd->uuid))
+ {
+ if (0 == strcmp ("question",
+ cd->type))
+ {
+ /* security question, answer must be a string and already ready */
+ json_t *janswer = json_object_get (challenge,
+ "answer");
+ const char *answer = json_string_value (janswer);
+
+ if (NULL == answer)
+ {
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "'answer' missing");
+ sctx_free (sctx);
+ return;
+ }
+ ret = ANASTASIS_challenge_answer (ci,
+ &sctx->ps,
+ answer,
+ &answer_feedback_cb,
+ sctx);
+ }
+ else
+ {
+ ret = ANASTASIS_challenge_start (ci,
+ &sctx->ps,
+ NULL, /* no answer yet */
+ &answer_feedback_cb,
+ sctx);
+ }
+ if (GNUNET_OK != ret)
+ {
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "Failed to begin answering challenge");
+ sctx_free (sctx);
+ return;
+ }
+ return; /* await answer feedback */
+ }
+ }
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "'uuid' not in list of challenges");
+ sctx_free (sctx);
+}
+
+
/**
* The user selected a challenge to be solved. Begin the solving
* process.
@@ -691,6 +827,322 @@ run_challenge (json_t *state,
const json_t *arguments,
ANASTASIS_ActionCallback cb,
void *cb_cls)
+{
+ struct SelectChallengeContext *sctx
+ = GNUNET_new (struct SelectChallengeContext);
+ json_t *rd;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("selected_challenge_uuid",
+ &sctx->uuid),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (NULL == arguments)
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "arguments missing");
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (state,
+ spec,
+ NULL, NULL))
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'selected_challenge_uuid' missing");
+ return NULL;
+ }
+ rd = json_object_get (state,
+ "recovery_document");
+ if (NULL == rd)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "run_challenge");
+ return NULL;
+ }
+ sctx->cb = cb;
+ sctx->cb_cls = cb_cls;
+ sctx->state = json_incref (state);
+ sctx->args = json_incref ((json_t*) arguments);
+ sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_,
+ rd,
+ &run_challenge_cb,
+ sctx,
+ &core_secret_cb,
+ sctx);
+ if (NULL == sctx->r)
+ {
+ json_decref (sctx->state);
+ json_decref (sctx->args);
+ GNUNET_free (sctx);
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' invalid");
+ return NULL;
+ }
+ sctx->ra.cleanup = &sctx_free;
+ sctx->ra.cleanup_cls = sctx;
+ return &sctx->ra;
+}
+
+
+/**
+ * The user selected a challenge to be solved. Handle the payment
+ * process.
+ *
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb functiont o call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
+ */
+static struct ANASTASIS_ReduxAction *
+pay_challenge (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
+{
+ struct SelectChallengeContext *sctx
+ = GNUNET_new (struct SelectChallengeContext);
+ json_t *rd;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("selected_challenge_uuid",
+ &sctx->uuid),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_JSON_Specification aspec[] = {
+ GNUNET_JSON_spec_fixed_auto ("payment_secret",
+ &sctx->ps),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (NULL == arguments)
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "arguments missing");
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (arguments,
+ aspec,
+ NULL, NULL))
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "'payment_secret' missing");
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (state,
+ spec,
+ NULL, NULL))
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'selected_challenge_uuid' missing");
+ return NULL;
+ }
+ rd = json_object_get (state,
+ "recovery_document");
+ if (NULL == rd)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "pay_challenge");
+ return NULL;
+ }
+ sctx->cb = cb;
+ sctx->cb_cls = cb_cls;
+ sctx->state = json_incref (state);
+ sctx->args = json_incref ((json_t*) arguments);
+ sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_,
+ rd,
+ &pay_challenge_cb,
+ sctx,
+ &core_secret_cb,
+ sctx);
+ if (NULL == sctx->r)
+ {
+ json_decref (sctx->state);
+ json_decref (sctx->args);
+ GNUNET_free (sctx);
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' invalid");
+ return NULL;
+ }
+ sctx->ra.cleanup = &sctx_free;
+ sctx->ra.cleanup_cls = sctx;
+ return &sctx->ra;
+}
+
+
+/**
+ * Callback which passes back the recovery document and its possible
+ * policies. Also passes back the version of the document for the user
+ * to check.
+ *
+ * We find the selected challenge and try to answer it (or begin
+ * the process).
+ *
+ * @param cls a `struct SelectChallengeContext *`
+ * @param ri recovery information struct which contains the policies
+ */
+static void
+select_challenge_cb (void *cls,
+ const struct ANASTASIS_RecoveryInformation *ri)
+{
+ struct SelectChallengeContext *sctx = cls;
+ const struct ANASTASIS_PaymentSecretP *psp = NULL;
+ struct ANASTASIS_PaymentSecretP ps;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("payment_secret",
+ &ps),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (NULL == ri)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "recovery information could not be deserialized");
+ sctx_free (sctx);
+ return;
+ }
+
+ /* Check if we got a payment_secret */
+ {
+ json_t *challenge = find_challenge_in_ri (sctx->state,
+ &sctx->uuid);
+
+ if (NULL == challenge)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "challenge not found");
+ sctx_free (sctx);
+ return;
+ }
+ if (NULL !=
+ json_object_get (challenge,
+ "payment_secret"))
+ {
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (sctx->args,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'payment_secret' malformed");
+ sctx_free (sctx);
+ return;
+ }
+ psp = &ps;
+ }
+ }
+
+ for (unsigned int i = 0; i<ri->cs_len; i++)
+ {
+ struct ANASTASIS_Challenge *ci = ri->cs[i];
+ const struct ANASTASIS_ChallengeDetails *cd;
+ int ret;
+
+ cd = ANASTASIS_challenge_get_details (ci);
+ if (cd->solved)
+ {
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "Selected challenge already solved");
+ sctx_free (sctx);
+ return;
+ }
+ if (0 ==
+ GNUNET_memcmp (&sctx->uuid,
+ &cd->uuid))
+ {
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (sctx->state,
+ "selected_challenge_uuid",
+ GNUNET_JSON_from_data_auto (&cd->uuid)));
+ if (0 == strcmp ("question",
+ cd->type))
+ {
+ /* security question, immediately request user to answer it */
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ }
+ /* trigger challenge */
+ ret = ANASTASIS_challenge_start (ci,
+ psp,
+ NULL, /* no answer */
+ &answer_feedback_cb,
+ sctx);
+ if (GNUNET_OK != ret)
+ {
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "Failed to begin answering challenge");
+ sctx_free (sctx);
+ return;
+ }
+ return; /* await answer feedback */
+ }
+ }
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "'uuid' not in list of challenges");
+ sctx_free (sctx);
+}
+
+
+/**
+ * The user selected a challenge to be solved. Begin the solving
+ * process.
+ *
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb functiont o call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
+ */
+static struct ANASTASIS_ReduxAction *
+select_challenge (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
{
struct SelectChallengeContext *sctx
= GNUNET_new (struct SelectChallengeContext);
@@ -737,7 +1189,7 @@ run_challenge (json_t *state,
sctx->args = json_incref ((json_t*) arguments);
sctx->r = ANASTASIS_recovery_deserialize (ANASTASIS_REDUX_ctx_,
rd,
- &run_challenge_cb,
+ &select_challenge_cb,
sctx,
&core_secret_cb,
sctx);
@@ -776,6 +1228,9 @@ back_challenge_solving (json_t *state,
void *cb_cls)
{
(void) arguments;
+ GNUNET_assert (0 ==
+ json_object_del (state,
+ "selected_challenge_uuid"));
set_state (state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
cb (cb_cls,
@@ -828,7 +1283,7 @@ ANASTASIS_recovery_action_ (json_t *state,
{
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
"select_challenge",
- &run_challenge
+ &select_challenge
},
{
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
@@ -838,7 +1293,7 @@ ANASTASIS_recovery_action_ (json_t *state,
{
ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING,
"pay",
- &run_challenge
+ &pay_challenge
},
{
ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING,
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-anastasis] branch master updated: clean up state machine in redux,
gnunet <=