summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-03-02 23:39:26 +0100
committerChristian Grothoff <christian@grothoff.org>2022-03-02 23:39:26 +0100
commitcf4b6ebd6de3370da4b16d2f1ef19a2a3d3d0b12 (patch)
treeeeec41a888f4f9cbdb77d6f01844ef20fccc2a43
parent81311476b804c054e4ee19c9b182f3b34357f88f (diff)
downloadanastasis-cf4b6ebd6de3370da4b16d2f1ef19a2a3d3d0b12.tar.gz
anastasis-cf4b6ebd6de3370da4b16d2f1ef19a2a3d3d0b12.tar.bz2
anastasis-cf4b6ebd6de3370da4b16d2f1ef19a2a3d3d0b12.zip
push new /truth/ API through the entire implementation
-rw-r--r--doc/sphinx/rest.rst19
-rw-r--r--src/authorization/anastasis_authorization_plugin_email.c11
-rw-r--r--src/authorization/anastasis_authorization_plugin_file.c4
-rw-r--r--src/authorization/anastasis_authorization_plugin_iban.c25
-rw-r--r--src/authorization/anastasis_authorization_plugin_post.c11
-rw-r--r--src/authorization/anastasis_authorization_plugin_sms.c11
-rw-r--r--src/backend/Makefile.am2
-rw-r--r--src/backend/anastasis-httpd.c51
-rw-r--r--src/backend/anastasis-httpd_truth-challenge.c36
-rw-r--r--src/backend/anastasis-httpd_truth-solve.c36
-rw-r--r--src/backend/anastasis-httpd_truth.c1702
-rw-r--r--src/backend/anastasis-httpd_truth.h25
-rw-r--r--src/include/anastasis.h309
-rw-r--r--src/include/anastasis_service.h323
-rw-r--r--src/include/anastasis_testing_lib.h33
-rw-r--r--src/lib/anastasis_recovery.c375
-rw-r--r--src/reducer/anastasis_api_recovery_redux.c474
-rw-r--r--src/restclient/Makefile.am1
-rw-r--r--src/restclient/anastasis_api_curl_defaults.c12
-rw-r--r--src/restclient/anastasis_api_truth_challenge.c50
-rw-r--r--src/restclient/anastasis_api_truth_solve.c2
-rw-r--r--src/testing/Makefile.am3
-rw-r--r--src/testing/test_anastasis.c10
-rw-r--r--src/testing/test_anastasis_api.c30
-rw-r--r--src/testing/testing_api_cmd_truth_challenge.c372
-rw-r--r--src/testing/testing_api_cmd_truth_solve.c (renamed from src/testing/testing_api_cmd_keyshare_lookup.c)194
-rw-r--r--src/testing/testing_cmd_challenge_answer.c230
27 files changed, 1465 insertions, 2886 deletions
diff --git a/doc/sphinx/rest.rst b/doc/sphinx/rest.rst
index 206eda7..4a5aad5 100644
--- a/doc/sphinx/rest.rst
+++ b/doc/sphinx/rest.rst
@@ -1,6 +1,6 @@
..
This file is part of Anastasis
- Copyright (C) 2019-2021 Anastasis SARL
+ Copyright (C) 2019-2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -789,13 +789,14 @@ charge per truth operation using GNU Taler.
.. ts:def:: ChallengeInstructionMessage
type ChallengeInstructionMessage =
+ | FileChallengeInstructionMessage
| IbanChallengeInstructionMessage
| PinChallengeInstructionMessage;
interface IbanChallengeInstructionMessage {
// What kind of challenge is this?
- method: "iban";
+ method: "IBAN_WIRE";
// How much should be wired?
amount: Amount;
@@ -817,11 +818,21 @@ charge per truth operation using GNU Taler.
interface PinChallengeInstructionMessage {
// What kind of challenge is this?
- method: "pin";
+ method: "TAN_SENT";
// Where was the PIN code sent? Note that this
// address will most likely have been obscured
// to improve privacy.
- address_hint: string;
+ tan_address_hint: string;
+
+ }
+
+ interface FileChallengeInstructionMessage {
+
+ // What kind of challenge is this?
+ method: "FILE_WRITTEN";
+
+ // Name of the file where the PIN code was written.
+ filename: string;
}
diff --git a/src/authorization/anastasis_authorization_plugin_email.c b/src/authorization/anastasis_authorization_plugin_email.c
index 2284988..78be9b6 100644
--- a/src/authorization/anastasis_authorization_plugin_email.c
+++ b/src/authorization/anastasis_authorization_plugin_email.c
@@ -463,12 +463,9 @@ email_process (struct ANASTASIS_AUTHORIZATION_State *as,
user = GNUNET_strndup (as->email,
len);
resp = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("code",
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED),
- GNUNET_JSON_pack_string ("hint",
- TALER_ErrorCode_get_hint (
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED)),
- GNUNET_JSON_pack_string ("detail",
+ GNUNET_JSON_pack_string ("challenge_type",
+ "TAN_SENT"),
+ GNUNET_JSON_pack_string ("tan_address_hint",
user));
GNUNET_free (user);
}
@@ -494,7 +491,7 @@ email_process (struct ANASTASIS_AUTHORIZATION_State *as,
"text/plain"));
}
mres = MHD_queue_response (connection,
- MHD_HTTP_FORBIDDEN,
+ MHD_HTTP_OK,
resp);
MHD_destroy_response (resp);
if (MHD_YES != mres)
diff --git a/src/authorization/anastasis_authorization_plugin_file.c b/src/authorization/anastasis_authorization_plugin_file.c
index 863db11..5186ae8 100644
--- a/src/authorization/anastasis_authorization_plugin_file.c
+++ b/src/authorization/anastasis_authorization_plugin_file.c
@@ -235,6 +235,8 @@ file_process (struct ANASTASIS_AUTHORIZATION_State *as,
"application/json"))
{
resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_string ("challenge_type",
+ "FILE_WRITTEN"),
GNUNET_JSON_pack_string ("filename",
as->filename));
}
@@ -260,7 +262,7 @@ file_process (struct ANASTASIS_AUTHORIZATION_State *as,
MHD_RESULT mres;
mres = MHD_queue_response (connection,
- MHD_HTTP_FORBIDDEN,
+ MHD_HTTP_OK,
resp);
MHD_destroy_response (resp);
if (MHD_YES != mres)
diff --git a/src/authorization/anastasis_authorization_plugin_iban.c b/src/authorization/anastasis_authorization_plugin_iban.c
index fec0d6d..bd58c51 100644
--- a/src/authorization/anastasis_authorization_plugin_iban.c
+++ b/src/authorization/anastasis_authorization_plugin_iban.c
@@ -340,23 +340,18 @@ respond_with_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
"Anastasis %llu",
(unsigned long long) as->code);
resp = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_string ("method",
- "iban"),
- GNUNET_JSON_pack_bool ("async",
- true),
+ GNUNET_JSON_pack_string ("challenge_type",
+ "IBAN_WIRE"),
GNUNET_JSON_pack_uint64 ("answer_code",
as->code),
- GNUNET_JSON_pack_object_steal (
- "details",
- GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("challenge_amount",
- &ctx->expected_amount),
- GNUNET_JSON_pack_string ("credit_iban",
- ctx->business_iban),
- GNUNET_JSON_pack_string ("business_name",
- ctx->business_name),
- GNUNET_JSON_pack_string ("wire_transfer_subject",
- subject))));
+ TALER_JSON_pack_amount ("challenge_amount",
+ &ctx->expected_amount),
+ GNUNET_JSON_pack_string ("credit_iban",
+ ctx->business_iban),
+ GNUNET_JSON_pack_string ("business_name",
+ ctx->business_name),
+ GNUNET_JSON_pack_string ("wire_transfer_subject",
+ subject));
}
else
{
diff --git a/src/authorization/anastasis_authorization_plugin_post.c b/src/authorization/anastasis_authorization_plugin_post.c
index 4f901d2..ad0ed15 100644
--- a/src/authorization/anastasis_authorization_plugin_post.c
+++ b/src/authorization/anastasis_authorization_plugin_post.c
@@ -524,12 +524,9 @@ post_process (struct ANASTASIS_AUTHORIZATION_State *as,
"application/json"))
{
resp = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("code",
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED),
- GNUNET_JSON_pack_string ("hint",
- TALER_ErrorCode_get_hint (
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED)),
- GNUNET_JSON_pack_string ("detail",
+ GNUNET_JSON_pack_string ("challenge_type",
+ "TAN_SENT"),
+ GNUNET_JSON_pack_string ("tan_address_hint",
zip));
}
else
@@ -549,7 +546,7 @@ post_process (struct ANASTASIS_AUTHORIZATION_State *as,
TALER_MHD_add_global_headers (resp);
}
mres = MHD_queue_response (connection,
- MHD_HTTP_FORBIDDEN,
+ MHD_HTTP_OK,
resp);
MHD_destroy_response (resp);
if (MHD_YES != mres)
diff --git a/src/authorization/anastasis_authorization_plugin_sms.c b/src/authorization/anastasis_authorization_plugin_sms.c
index 98152ef..6598d29 100644
--- a/src/authorization/anastasis_authorization_plugin_sms.c
+++ b/src/authorization/anastasis_authorization_plugin_sms.c
@@ -455,12 +455,9 @@ sms_process (struct ANASTASIS_AUTHORIZATION_State *as,
"application/json"))
{
resp = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("code",
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED),
- GNUNET_JSON_pack_string ("hint",
- TALER_ErrorCode_get_hint (
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED)),
- GNUNET_JSON_pack_string ("detail",
+ GNUNET_JSON_pack_string ("challenge_type",
+ "TAN_SENT"),
+ GNUNET_JSON_pack_string ("tan_address_hint",
end));
}
else
@@ -484,7 +481,7 @@ sms_process (struct ANASTASIS_AUTHORIZATION_State *as,
"text/plain"));
}
mres = MHD_queue_response (connection,
- MHD_HTTP_FORBIDDEN,
+ MHD_HTTP_OK,
resp);
MHD_destroy_response (resp);
if (MHD_YES != mres)
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 83877bc..db37478 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -20,7 +20,7 @@ anastasis_httpd_SOURCES = \
anastasis-httpd_policy.c anastasis-httpd_policy.h \
anastasis-httpd_policy-meta.c anastasis-httpd_policy-meta.h \
anastasis-httpd_policy-upload.c \
- anastasis-httpd_truth.c anastasis-httpd_truth.h \
+ anastasis-httpd_truth.h \
anastasis-httpd_terms.c anastasis-httpd_terms.h \
anastasis-httpd_config.c anastasis-httpd_config.h \
anastasis-httpd_truth-challenge.c \
diff --git a/src/backend/anastasis-httpd.c b/src/backend/anastasis-httpd.c
index 0c9d957..4428851 100644
--- a/src/backend/anastasis-httpd.c
+++ b/src/backend/anastasis-httpd.c
@@ -439,17 +439,19 @@ url_handler (void *cls,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"truth UUID");
}
- if ( (NULL == end) &&
- (0 == strcmp (method,
- MHD_HTTP_METHOD_GET)) )
- {
- return AH_handler_truth_get (connection,
- &tu,
- hc);
- }
- if ( (NULL == end) &&
- (0 == strcmp (method,
- MHD_HTTP_METHOD_POST)) )
+ if ( (NULL != end) &&
+ (0 != strcmp (end, "/solve")) &&
+ (0 != strcmp (end, "/challenge")) )
+ return TMH_MHD_handler_static_response (&h404,
+ connection);
+ if (0 == strcmp (method,
+ MHD_HTTP_METHOD_OPTIONS))
+ return TALER_MHD_reply_cors_preflight (connection);
+ if (0 != strcmp (method,
+ MHD_HTTP_METHOD_POST))
+ return TMH_MHD_handler_static_response (&h405,
+ connection);
+ if (NULL == end)
{
return AH_handler_truth_post (connection,
hc,
@@ -457,11 +459,8 @@ url_handler (void *cls,
upload_data,
upload_data_size);
}
- if ( (NULL != end) &&
- (0 == strcmp (end,
- "/solve")) &&
- (0 == strcmp (method,
- MHD_HTTP_METHOD_POST)) )
+ if (0 == strcmp (end,
+ "/solve"))
{
return AH_handler_truth_solve (connection,
hc,
@@ -469,11 +468,8 @@ url_handler (void *cls,
upload_data,
upload_data_size);
}
- if ( (NULL != end) &&
- (0 == strcmp (end,
- "/challenge")) &&
- (0 == strcmp (method,
- MHD_HTTP_METHOD_POST)) )
+ if (0 == strcmp (end,
+ "/challenge"))
{
return AH_handler_truth_challenge (connection,
hc,
@@ -481,14 +477,9 @@ url_handler (void *cls,
upload_data,
upload_data_size);
}
- if (0 == strcmp (method,
- MHD_HTTP_METHOD_OPTIONS))
- {
- return TALER_MHD_reply_cors_preflight (connection);
- }
- return TMH_MHD_handler_static_response (&h405,
- connection);
- }
+ /* should be impossible to get here */
+ GNUNET_assert (0);
+ } /* end of "/truth/" prefix */
path_matched = false;
for (unsigned int i = 0; NULL != handlers[i].url; i++)
{
@@ -531,7 +522,6 @@ do_shutdown (void *cls)
{
(void) cls;
AH_resume_all_bc ();
- AH_truth_shutdown ();
AH_truth_challenge_shutdown ();
AH_truth_solve_shutdown ();
AH_truth_upload_shutdown ();
@@ -1030,7 +1020,6 @@ main (int argc,
"CERTTYPE",
"type of the TLS client certificate, defaults to PEM if not specified",
&certtype),
-
GNUNET_GETOPT_OPTION_END
};
diff --git a/src/backend/anastasis-httpd_truth-challenge.c b/src/backend/anastasis-httpd_truth-challenge.c
index c583403..65dc244 100644
--- a/src/backend/anastasis-httpd_truth-challenge.c
+++ b/src/backend/anastasis-httpd_truth-challenge.c
@@ -981,33 +981,6 @@ AH_handler_truth_challenge (
gc->connection = connection;
gc->truth_uuid = *truth_uuid;
gc->hc->cc = &request_done;
- {
- const char *pay_id;
-
- pay_id = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
- if (NULL != pay_id)
- {
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (
- pay_id,
- strlen (pay_id),
- &gc->payment_identifier,
- sizeof (struct ANASTASIS_PaymentSecretP)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
- }
- gc->payment_identifier_provided = true;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Client provided payment identifier `%s'\n",
- pay_id);
- }
- }
{
const char *long_poll_timeout_ms;
@@ -1104,6 +1077,9 @@ AH_handler_truth_challenge (
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("truth_decryption_key",
&gc->truth_key),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("payment_secret",
+ &gc->payment_identifier)),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
@@ -1121,6 +1097,12 @@ AH_handler_truth_challenge (
GNUNET_break_op (0);
return MHD_YES; /* failure */
}
+ gc->payment_identifier_provided
+ = ! GNUNET_is_zero (&gc->payment_identifier);
+ if (gc->payment_identifier_provided)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Client provided payment identifier `%s'\n",
+ TALER_B2S (&gc->payment_identifier));
}
}
diff --git a/src/backend/anastasis-httpd_truth-solve.c b/src/backend/anastasis-httpd_truth-solve.c
index 577ec50..a452d8e 100644
--- a/src/backend/anastasis-httpd_truth-solve.c
+++ b/src/backend/anastasis-httpd_truth-solve.c
@@ -1049,33 +1049,6 @@ AH_handler_truth_solve (
gc->connection = connection;
gc->truth_uuid = *truth_uuid;
gc->hc->cc = &request_done;
- {
- const char *pay_id;
-
- pay_id = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
- if (NULL != pay_id)
- {
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (
- pay_id,
- strlen (pay_id),
- &gc->payment_identifier,
- sizeof (struct ANASTASIS_PaymentSecretP)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
- }
- gc->payment_identifier_provided = true;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Client provided payment identifier `%s'\n",
- pay_id);
- }
- }
{
const char *long_poll_timeout_ms;
@@ -1174,6 +1147,9 @@ AH_handler_truth_solve (
&gc->truth_key),
GNUNET_JSON_spec_fixed_auto ("h_response",
&gc->challenge_response),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("payment_secret",
+ &gc->payment_identifier)),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
@@ -1191,6 +1167,12 @@ AH_handler_truth_solve (
GNUNET_break_op (0);
return MHD_YES; /* failure */
}
+ gc->payment_identifier_provided
+ = ! GNUNET_is_zero (&gc->payment_identifier);
+ if (gc->payment_identifier_provided)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Client provided payment identifier `%s'\n",
+ TALER_B2S (&gc->payment_identifier));
}
}
diff --git a/src/backend/anastasis-httpd_truth.c b/src/backend/anastasis-httpd_truth.c
deleted file mode 100644
index ba6837a..0000000
--- a/src/backend/anastasis-httpd_truth.c
+++ /dev/null
@@ -1,1702 +0,0 @@
-/*
- This file is part of Anastasis
- Copyright (C) 2019, 2021 Anastasis SARL
-
- Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero 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 Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file anastasis-httpd_truth.c
- * @brief functions to handle incoming requests on /truth
- * @author Dennis Neufeld
- * @author Dominik Meister
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "anastasis-httpd.h"
-#include "anastasis_service.h"
-#include "anastasis-httpd_truth.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_rest_lib.h>
-#include "anastasis_authorization_lib.h"
-#include <taler/taler_merchant_service.h>
-#include <taler/taler_json_lib.h>
-
-/**
- * What is the maximum frequency at which we allow
- * clients to attempt to answer security questions?
- */
-#define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_SECONDS, 30)
-
-/**
- * How long should the wallet check for auto-refunds before giving up?
- */
-#define AUTO_REFUND_TIMEOUT GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_MINUTES, 2)
-
-
-/**
- * How many retries do we allow per code?
- */
-#define INITIAL_RETRY_COUNTER 3
-
-
-struct GetContext
-{
-
- /**
- * Payment Identifier
- */
- struct ANASTASIS_PaymentSecretP payment_identifier;
-
- /**
- * Public key of the challenge which is solved.
- */
- struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
-
- /**
- * Key to decrypt the truth.
- */
- struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
-
- /**
- * Cost for paying the challenge.
- */
- struct TALER_Amount challenge_cost;
-
- /**
- * Our handler context.
- */
- struct TM_HandlerContext *hc;
-
- /**
- * Kept in DLL for shutdown handling while suspended.
- */
- struct GetContext *next;
-
- /**
- * Kept in DLL for shutdown handling while suspended.
- */
- struct GetContext *prev;
-
- /**
- * Connection handle for closing or resuming
- */
- struct MHD_Connection *connection;
-
- /**
- * Reference to the authorization plugin which was loaded
- */
- struct ANASTASIS_AuthorizationPlugin *authorization;
-
- /**
- * Status of the authorization
- */
- struct ANASTASIS_AUTHORIZATION_State *as;
-
- /**
- * Used while we are awaiting proposal creation.
- */
- struct TALER_MERCHANT_PostOrdersHandle *po;
-
- /**
- * Used while we are waiting payment.
- */
- struct TALER_MERCHANT_OrderMerchantGetHandle *cpo;
-
- /**
- * HTTP response code to use on resume, if non-NULL.
- */
- struct MHD_Response *resp;
-
- /**
- * Our entry in the #to_heap, or NULL.
- */
- struct GNUNET_CONTAINER_HeapNode *hn;
-
- /**
- * Challenge response we got from the request.
- */
- struct GNUNET_HashCode challenge_response;
-
- /**
- * How long do we wait at most for payment or
- * authorization?
- */
- struct GNUNET_TIME_Absolute timeout;
-
- /**
- * Random authorization code we are using.
- */
- uint64_t code;
-
- /**
- * HTTP response code to use on resume, if resp is set.
- */
- unsigned int response_code;
-
- /**
- * true if client provided a payment secret / order ID?
- */
- bool payment_identifier_provided;
-
- /**
- * True if this entry is in the #gc_head DLL.
- */
- bool in_list;
-
- /**
- * True if this entry is currently suspended.
- */
- bool suspended;
-
- /**
- * Did the request include a response?
- */
- bool have_response;
-
-};
-
-/**
- * Information we track for refunds.
- */
-struct RefundEntry
-{
- /**
- * Kept in a DLL.
- */
- struct RefundEntry *next;
-
- /**
- * Kept in a DLL.
- */
- struct RefundEntry *prev;
-
- /**
- * Operation handle.
- */
- struct TALER_MERCHANT_OrderRefundHandle *ro;
-
- /**
- * Which order is being refunded.
- */
- char *order_id;
-
- /**
- * Payment Identifier
- */
- struct ANASTASIS_PaymentSecretP payment_identifier;
-
- /**
- * Public key of the challenge which is solved.
- */
- struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
-};
-
-
-/**
- * Head of linked list of active refund operations.
- */
-static struct RefundEntry *re_head;
-
-/**
- * Tail of linked list of active refund operations.
- */
-static struct RefundEntry *re_tail;
-
-/**
- * Head of linked list over all authorization processes
- */
-static struct GetContext *gc_head;
-
-/**
- * Tail of linked list over all authorization processes
- */
-static struct GetContext *gc_tail;
-
-/**
- * Task running #do_timeout().
- */
-static struct GNUNET_SCHEDULER_Task *to_task;
-
-
-/**
- * Generate a response telling the client that answering this
- * challenge failed because the rate limit has been exceeded.
- *
- * @param gc request to answer for
- * @return MHD status code
- */
-static MHD_RESULT
-reply_rate_limited (const struct GetContext *gc)
-{
- return TALER_MHD_REPLY_JSON_PACK (
- gc->connection,
- MHD_HTTP_TOO_MANY_REQUESTS,
- TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED),
- GNUNET_JSON_pack_uint64 ("request_limit",
- gc->authorization->retry_counter),
- GNUNET_JSON_pack_time_rel ("request_frequency",
- gc->authorization->code_rotation_period));
-}
-
-
-/**
- * Timeout requests that are past their due date.
- *
- * @param cls NULL
- */
-static void
-do_timeout (void *cls)
-{
- struct GetContext *gc;
-
- (void) cls;
- to_task = NULL;
- while (NULL !=
- (gc = GNUNET_CONTAINER_heap_peek (AH_to_heap)))
- {
- if (GNUNET_TIME_absolute_is_future (gc->timeout))
- break;
- if (gc->suspended)
- {
- /* Test needed as we may have a "concurrent"
- wakeup from another task that did not clear
- this entry from the heap before the
- response process concluded. */
- gc->suspended = false;
- MHD_resume_connection (gc->connection);
- }
- GNUNET_assert (NULL != gc->hn);
- gc->hn = NULL;
- GNUNET_assert (gc ==
- GNUNET_CONTAINER_heap_remove_root (AH_to_heap));
- }
- if (NULL == gc)
- return;
- to_task = GNUNET_SCHEDULER_add_at (gc->timeout,
- &do_timeout,
- NULL);
-}
-
-
-void
-AH_truth_shutdown (void)
-{
- struct GetContext *gc;
- struct RefundEntry *re;
-
- while (NULL != (re = re_head))
- {
- GNUNET_CONTAINER_DLL_remove (re_head,
- re_tail,
- re);
- if (NULL != re->ro)
- {
- TALER_MERCHANT_post_order_refund_cancel (re->ro);
- re->ro = NULL;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Refund `%s' failed due to shutdown\n",
- re->order_id);
- GNUNET_free (re->order_id);
- GNUNET_free (re);
- }
-
- while (NULL != (gc = gc_head))
- {
- GNUNET_CONTAINER_DLL_remove (gc_head,
- gc_tail,
- gc);
- gc->in_list = false;
- if (NULL != gc->cpo)
- {
- TALER_MERCHANT_merchant_order_get_cancel (gc->cpo);
- gc->cpo = NULL;
- }
- if (NULL != gc->po)
- {
- TALER_MERCHANT_orders_post_cancel (gc->po);
- gc->po = NULL;
- }
- if (gc->suspended)
- {
- gc->suspended = false;
- MHD_resume_connection (gc->connection);
- }
- if (NULL != gc->as)
- {
- gc->authorization->cleanup (gc->as);
- gc->as = NULL;
- gc->authorization = NULL;
- }
- }
- ANASTASIS_authorization_plugin_shutdown ();
- if (NULL != to_task)
- {
- GNUNET_SCHEDULER_cancel (to_task);
- to_task = NULL;
- }
-}
-
-
-/**
- * Callback to process a POST /orders/ID/refund request
- *
- * @param cls closure with a `struct RefundEntry *`
- * @param hr HTTP response details
- * @param taler_refund_uri the refund uri offered to the wallet
- * @param h_contract hash of the contract a Browser may need to authorize
- * obtaining the HTTP response.
- */
-static void
-refund_cb (
- void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const char *taler_refund_uri,
- const struct TALER_PrivateContractHashP *h_contract)
-{
- struct RefundEntry *re = cls;
-
- re->ro = NULL;
- switch (hr->http_status)
- {
- case MHD_HTTP_OK:
- {
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Refund `%s' succeeded\n",
- re->order_id);
- qs = db->record_challenge_refund (db->cls,
- &re->truth_uuid,
- &re->payment_identifier);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- break;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_break (0);
- break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- break;
- }
- }
- break;
- default:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Refund `%s' failed with HTTP status %u: %s (#%u)\n",
- re->order_id,
- hr->http_status,
- hr->hint,
- (unsigned int) hr->ec);
- break;
- }
- GNUNET_CONTAINER_DLL_remove (re_head,
- re_tail,
- re);
- GNUNET_free (re->order_id);
- GNUNET_free (re);
-}
-
-
-/**
- * Start to give a refund for the challenge created by @a gc.
- *
- * @param gc request where we failed and should now grant a refund for
- */
-static void
-begin_refund (const struct GetContext *gc)
-{
- struct RefundEntry *re;
-
- re = GNUNET_new (struct RefundEntry);
- re->order_id = GNUNET_STRINGS_data_to_string_alloc (
- &gc->payment_identifier,
- sizeof (gc->payment_identifier));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Challenge execution failed, triggering refund for order `%s'\n",
- re->order_id);
- re->payment_identifier = gc->payment_identifier;
- re->truth_uuid = gc->truth_uuid;
- re->ro = TALER_MERCHANT_post_order_refund (AH_ctx,
- AH_backend_url,
- re->order_id,
- &gc->challenge_cost,
- "failed to issue challenge",
- &refund_cb,
- re);
- if (NULL == re->ro)
- {
- GNUNET_break (0);
- GNUNET_free (re->order_id);
- GNUNET_free (re);
- return;
- }
- GNUNET_CONTAINER_DLL_insert (re_head,
- re_tail,
- re);
-}
-
-
-/**
- * Callback used to notify the application about completed requests.
- * Cleans up the requests data structures.
- *
- * @param hc
- */
-static void
-request_done (struct TM_HandlerContext *hc)
-{
- struct GetContext *gc = hc->ctx;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request completed\n");
- if (NULL == gc)
- return;
- hc->cc = NULL;
- GNUNET_assert (! gc->suspended);
- if (gc->in_list)
- {
- GNUNET_CONTAINER_DLL_remove (gc_head,
- gc_tail,
- gc);
- gc->in_list = false;
- }
- if (NULL != gc->hn)
- {
- GNUNET_assert (gc ==
- GNUNET_CONTAINER_heap_remove_node (gc->hn));
- gc->hn = NULL;
- }
- if (NULL != gc->as)
- {
- gc->authorization->cleanup (gc->as);
- gc->authorization = NULL;
- gc->as = NULL;
- }
- if (NULL != gc->cpo)
- {
- TALER_MERCHANT_merchant_order_get_cancel (gc->cpo);
- gc->cpo = NULL;
- }
- if (NULL != gc->po)
- {
- TALER_MERCHANT_orders_post_cancel (gc->po);
- gc->po = NULL;
- }
- GNUNET_free (gc);
- hc->ctx = NULL;
-}
-
-
-/**
- * Transmit a payment request for @a order_id on @a connection
- *
- * @param gc context to make payment request for
- */
-static void
-make_payment_request (struct GetContext *gc)
-{
- struct MHD_Response *resp;
-
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- GNUNET_assert (NULL != resp);
- TALER_MHD_add_global_headers (resp);
- {
- char *hdr;
- char *order_id;
- const char *pfx;
- const char *hn;
-
- if (0 == strncasecmp ("https://",
- AH_backend_url,
- strlen ("https://")))
- {
- pfx = "taler://";
- hn = &AH_backend_url[strlen ("https://")];
- }
- else if (0 == strncasecmp ("http://",
- AH_backend_url,
- strlen ("http://")))
- {
- pfx = "taler+http://";
- hn = &AH_backend_url[strlen ("http://")];
- }
- else
- {
- /* This invariant holds as per check in anastasis-httpd.c */
- GNUNET_assert (0);
- }
- /* This invariant holds as per check in anastasis-httpd.c */
- GNUNET_assert (0 != strlen (hn));
-
- order_id = GNUNET_STRINGS_data_to_string_alloc (
- &gc->payment_identifier,
- sizeof (gc->payment_identifier));
- GNUNET_asprintf (&hdr,
- "%spay/%s%s/",
- pfx,
- hn,
- order_id);
- GNUNET_free (order_id);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Sending payment request `%s'\n",
- hdr);
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (resp,
- ANASTASIS_HTTP_HEADER_TALER,
- hdr));
- GNUNET_free (hdr);
- }
- gc->resp = resp;
- gc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
-}
-
-
-/**
- * Callbacks of this type are used to serve the result of submitting a
- * /contract request to a merchant.
- *
- * @param cls our `struct GetContext`
- * @param por response details
- */
-static void
-proposal_cb (void *cls,
- const struct TALER_MERCHANT_PostOrdersReply *por)
-{
- struct GetContext *gc = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- gc->po = NULL;
- GNUNET_assert (gc->in_list);
- GNUNET_CONTAINER_DLL_remove (gc_head,
- gc_tail,
- gc);
- gc->in_list = false;
- GNUNET_assert (gc->suspended);
- gc->suspended = false;
- MHD_resume_connection (gc->connection);
- AH_trigger_daemon (NULL);
- if (MHD_HTTP_OK != por->hr.http_status)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Backend returned status %u/%d\n",
- por->hr.http_status,
- (int) por->hr.ec);
- GNUNET_break (0);
- gc->resp = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("code",
- TALER_EC_ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR),
- GNUNET_JSON_pack_string ("hint",
- "Failed to setup order with merchant backend"),
- GNUNET_JSON_pack_uint64 ("backend-ec",
- por->hr.ec),
- GNUNET_JSON_pack_uint64 ("backend-http-status",
- por->hr.http_status),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_steal ("backend-reply",
- (json_t *) por->hr.reply)));
- gc->response_code = MHD_HTTP_BAD_GATEWAY;
- return;
- }
- qs = db->record_challenge_payment (db->cls,
- &gc->truth_uuid,
- &gc->payment_identifier,
- &gc->challenge_cost);
- if (0 >= qs)
- {
- GNUNET_break (0);
- gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
- "record challenge payment");
- gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Setup fresh order, creating payment request\n");
- make_payment_request (gc);
-}
-
-
-/**
- * Callback to process a GET /check-payment request
- *
- * @param cls our `struct GetContext`
- * @param hr HTTP response details
- * @param osr order status
- */
-static void
-check_payment_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_MERCHANT_OrderStatusResponse *osr)
-
-{
- struct GetContext *gc = cls;
-
- gc->cpo = NULL;
- GNUNET_assert (gc->in_list);
- GNUNET_CONTAINER_DLL_remove (gc_head,
- gc_tail,
- gc);
- gc->in_list = false;
- GNUNET_assert (gc->suspended);
- gc->suspended = false;
- MHD_resume_connection (gc->connection);
- AH_trigger_daemon (NULL);
-
- switch (hr->http_status)
- {
- case MHD_HTTP_OK:
- GNUNET_assert (NULL != osr);
- break;
- case MHD_HTTP_NOT_FOUND:
- /* We created this order before, how can it be not found now? */
- GNUNET_break (0);
- gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_TRUTH_ORDER_DISAPPEARED,
- NULL);
- gc->response_code = MHD_HTTP_BAD_GATEWAY;
- return;
- case MHD_HTTP_BAD_GATEWAY:
- gc->resp = TALER_MHD_make_error (
- TALER_EC_ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD,
- NULL);
- gc->response_code = MHD_HTTP_BAD_GATEWAY;
- return;
- case MHD_HTTP_GATEWAY_TIMEOUT:
- gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT,
- "Timeout check payment status");
- GNUNET_assert (NULL != gc->resp);
- gc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
- return;
- default:
- {
- char status[14];
-
- GNUNET_snprintf (status,
- sizeof (status),
- "%u",
- hr->http_status);
- gc->resp = TALER_MHD_make_error (
- TALER_EC_ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS,
- status);
- GNUNET_assert (NULL != gc->resp);
- gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- return;
- }
- }
-
- switch (osr->status)
- {
- case TALER_MERCHANT_OSC_PAID:
- {
- enum GNUNET_DB_QueryStatus qs;
-
- qs = db->update_challenge_payment (db->cls,
- &gc->truth_uuid,
- &gc->payment_identifier);
- if (0 <= qs)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order has been paid, continuing with request processing\n");
- return; /* continue as planned */
- }
- GNUNET_break (0);
- gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
- "update challenge payment");
- gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- return; /* continue as planned */
- }
- case TALER_MERCHANT_OSC_CLAIMED:
- case TALER_MERCHANT_OSC_UNPAID:
- /* repeat payment request */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order remains unpaid, sending payment request again\n");
- make_payment_request (gc);
- return;
- }
- /* should never get here */
- GNUNET_break (0);
-}
-
-
-/**
- * Helper function used to ask our backend to begin processing a
- * payment for the user's account. May perform asynchronous
- * operations by suspending the connection if required.
- *
- * @param gc context to begin payment for.
- * @return MHD status code
- */
-static MHD_RESULT
-begin_payment (struct GetContext *gc)
-{
- enum GNUNET_DB_QueryStatus qs;
- char *order_id;
-
- qs = db->lookup_challenge_payment (db->cls,
- &gc->truth_uuid,
- &gc->payment_identifier);
- if (qs < 0)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "lookup challenge payment");
- }
- GNUNET_assert (! gc->in_list);
- gc->in_list = true;
- GNUNET_CONTAINER_DLL_insert (gc_tail,
- gc_head,
- gc);
- GNUNET_assert (! gc->suspended);
- gc->suspended = true;
- MHD_suspend_connection (gc->connection);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- /* We already created the order, check if it was paid */
- struct GNUNET_TIME_Relative timeout;
-
- order_id = GNUNET_STRINGS_data_to_string_alloc (
- &gc->payment_identifier,
- sizeof (gc->payment_identifier));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order exists, checking payment status for order `%s'\n",
- order_id);
- timeout = GNUNET_TIME_absolute_get_remaining (gc->timeout);
- gc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx,
- AH_backend_url,
- order_id,
- NULL /* NOT session-bound */,
- false,
- timeout,
- &check_payment_cb,
- gc);
- }
- else
- {
- /* Create a fresh order */
- json_t *order;
- struct GNUNET_TIME_Timestamp pay_deadline;
-
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
- &gc->payment_identifier,
- sizeof (struct ANASTASIS_PaymentSecretP));
- order_id = GNUNET_STRINGS_data_to_string_alloc (
- &gc->payment_identifier,
- sizeof (gc->payment_identifier));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Creating fresh order `%s'\n",
- order_id);
- pay_deadline = GNUNET_TIME_relative_to_timestamp (
- ANASTASIS_CHALLENGE_OFFER_LIFETIME);
- order = GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("amount",
- &gc->challenge_cost),
- GNUNET_JSON_pack_string ("summary",
- "challenge fee for anastasis service"),
- GNUNET_JSON_pack_string ("order_id",
- order_id),
- GNUNET_JSON_pack_time_rel ("auto_refund",
- AUTO_REFUND_TIMEOUT),
- GNUNET_JSON_pack_timestamp ("pay_deadline",
- pay_deadline));
- gc->po = TALER_MERCHANT_orders_post2 (AH_ctx,
- AH_backend_url,
- order,
- AUTO_REFUND_TIMEOUT,
- NULL, /* no payment target */
- 0,
- NULL, /* no inventory products */
- 0,
- NULL, /* no uuids */
- false, /* do NOT require claim token */
- &proposal_cb,
- gc);
- json_decref (order);
- }
- GNUNET_free (order_id);
- AH_trigger_curl ();
- return MHD_YES;
-}
-
-
-/**
- * Load encrypted keyshare from db and return it to the client.
- *
- * @param truth_uuid UUID to the truth for the looup
- * @param connection the connection to respond upon
- * @return MHD status code
- */
-static MHD_RESULT
-return_key_share (
- const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
- struct MHD_Connection *connection)
-{
- struct ANASTASIS_CRYPTO_EncryptedKeyShareP encrypted_keyshare;
-
- {
- enum GNUNET_DB_QueryStatus qs;
-
- qs = db->get_key_share (db->cls,
- truth_uuid,
- &encrypted_keyshare);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get key share");
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_ANASTASIS_TRUTH_KEY_SHARE_GONE,
- NULL);
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- break;
- }
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning key share\n");
- {
- struct MHD_Response *resp;
- MHD_RESULT ret;
-
- resp = MHD_create_response_from_buffer (sizeof (encrypted_keyshare),
- &encrypted_keyshare,
- MHD_RESPMEM_MUST_COPY);
- TALER_MHD_add_global_headers (resp);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- return ret;
- }
-}
-
-
-/**
- * Mark @a gc as suspended and update the respective
- * data structures and jobs.
- *
- * @param[in,out] gc context of the suspended operation
- */
-static void
-gc_suspended (struct GetContext *gc)
-{
- gc->suspended = true;
- if (NULL == AH_to_heap)
- AH_to_heap = GNUNET_CONTAINER_heap_create (
- GNUNET_CONTAINER_HEAP_ORDER_MIN);
- gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
- gc,
- gc->timeout.abs_value_us);
- if (NULL != to_task)
- {
- GNUNET_SCHEDULER_cancel (to_task);
- to_task = NULL;
- }
- {
- struct GetContext *rn;
-
- rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
- to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
- &do_timeout,
- NULL);
- }
-}
-
-
-/**
- * Run the authorization method-specific 'process' function and continue
- * based on its result with generating an HTTP response.
- *
- * @param connection the connection we are handling
- * @param gc our overall handler context
- */
-static MHD_RESULT
-run_authorization_process (struct MHD_Connection *connection,
- struct GetContext *gc)
-{
- enum ANASTASIS_AUTHORIZATION_Result ret;
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_assert (! gc->suspended);
- ret = gc->authorization->process (gc->as,
- gc->timeout,
- connection);
- switch (ret)
- {
- case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
- /* Challenge sent successfully */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Authorization request sent successfully\n");
- qs = db->mark_challenge_sent (db->cls,
- &gc->payment_identifier,
- &gc->truth_uuid,
- gc->code);
- GNUNET_break (0 < qs);
- gc->authorization->cleanup (gc->as);
- gc->as = NULL;
- return MHD_YES;
- case ANASTASIS_AUTHORIZATION_RES_FAILED:
- if (gc->payment_identifier_provided)
- {
- begin_refund (gc);
- }
- gc->authorization->cleanup (gc->as);
- gc->as = NULL;
- return MHD_YES;
- case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
- /* connection was suspended */
- gc_suspended (gc);
- return MHD_YES;
- case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
- /* Challenge sent successfully */
- qs = db->mark_challenge_sent (db->cls,
- &gc->payment_identifier,
- &gc->truth_uuid,
- gc->code);
- GNUNET_break (0 < qs);
- gc->authorization->cleanup (gc->as);
- gc->as = NULL;
- return MHD_NO;
- case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
- gc->authorization->cleanup (gc->as);
- gc->as = NULL;
- return MHD_NO;
- case ANASTASIS_AUTHORIZATION_RES_FINISHED:
- GNUNET_assert (! gc->suspended);
- gc->authorization->cleanup (gc->as);
- gc->as = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Resuming with authorization successful!\n");
- if (gc->in_list)
- {
- GNUNET_CONTAINER_DLL_remove (gc_head,
- gc_tail,
- gc);
- gc->in_list = false;
- }
- return MHD_YES;
- }
- GNUNET_break (0);
- return MHD_NO;
-}
-
-
-/**
- * Use the database to rate-limit queries to the authentication
- * procedure, but without actually storing 'real' challenge codes.
- *
- * @param[in,out] gc context to rate limit requests for
- * @return #GNUNET_OK if rate-limiting passes,
- * #GNUNET_NO if a reply was sent (rate limited)
- * #GNUNET_SYSERR if we failed and no reply
- * was queued
- */
-static enum GNUNET_GenericReturnValue
-rate_limit (struct GetContext *gc)
-{
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Timestamp rt;
- uint64_t code;
- enum ANASTASIS_DB_CodeStatus cs;
- struct GNUNET_HashCode hc;
- bool satisfied;
- uint64_t dummy;
-
- rt = GNUNET_TIME_UNIT_FOREVER_TS;
- qs = db->create_challenge_code (db->cls,
- &gc->truth_uuid,
- MAX_QUESTION_FREQ,
- GNUNET_TIME_UNIT_HOURS,
- INITIAL_RETRY_COUNTER,
- &rt,
- &code);
- if (0 > qs)
- {
- GNUNET_break (0 < qs);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "create_challenge_code (for rate limiting)"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- return (MHD_YES ==
- reply_rate_limited (gc))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- /* decrement trial counter */
- ANASTASIS_hash_answer (code + 1, /* always use wrong answer */
- &hc);
- cs = db->verify_challenge_code (db->cls,
- &gc->truth_uuid,
- &hc,
- &dummy,
- &satisfied);
- switch (cs)
- {
- case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
- /* good, what we wanted */
- return GNUNET_OK;
- case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
- case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "verify_challenge_code"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
- return (MHD_YES ==
- reply_rate_limited (gc))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
- /* this should be impossible, we used code+1 */
- GNUNET_assert (0);
- }
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Handle special case of a security question where we do not
- * generate a code. Rate limits answers against brute forcing.
- *
- * @param[in,out] gc request to handle
- * @param decrypted_truth hash to check against
- * @param decrypted_truth_size number of bytes in @a decrypted_truth
- * @return MHD status code
- */
-static MHD_RESULT
-handle_security_question (struct GetContext *gc,
- const void *decrypted_truth,
- size_t decrypted_truth_size)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling security question challenge\n");
- if (! gc->have_response)
- {
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
- NULL);
- }
- /* rate limit */
- {
- enum GNUNET_GenericReturnValue ret;
-
- ret = rate_limit (gc);
- if (GNUNET_OK != ret)
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- /* check reply matches truth */
- if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
- (0 != memcmp (&gc->challenge_response,
- decrypted_truth,
- decrypted_truth_size)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Wrong answer provided to secure question had %u bytes, wanted %u\n",
- (unsigned int) decrypted_truth_size,
- (unsigned int) sizeof (struct GNUNET_HashCode));
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
- NULL);
- }
- /* good, return the key share */
- return return_key_share (&gc->truth_uuid,
- gc->connection);
-}
-
-
-/**
- * Handle special case of an answer being directly checked by the
- * plugin and not by our database. Rate limits answers against brute
- * forcing.
- *
- * @param[in,out] gc request to handle
- * @param decrypted_truth hash to check against
- * @param decrypted_truth_size number of bytes in @a decrypted_truth
- * @return MHD status code
- */
-static MHD_RESULT
-direct_validation (struct GetContext *gc,
- const void *decrypted_truth,
- size_t decrypted_truth_size)
-{
- /* Non-random code, call plugin directly! */
- enum ANASTASIS_AUTHORIZATION_Result aar;
- enum GNUNET_GenericReturnValue res;
-
- res = rate_limit (gc);
- if (GNUNET_OK != res)
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- gc->as = gc->authorization->start (gc->authorization->cls,
- &AH_trigger_daemon,
- NULL,
- &gc->truth_uuid,
- 0LLU,
- decrypted_truth,
- decrypted_truth_size);
- if (NULL == gc->as)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
- NULL);
- }
- aar = gc->authorization->process (gc->as,
- GNUNET_TIME_UNIT_ZERO_ABS,
- gc->connection);
- switch (aar)
- {
- case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
- GNUNET_break (0);
- return MHD_YES;
- case ANASTASIS_AUTHORIZATION_RES_FAILED:
- return MHD_YES;
- case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
- gc_suspended (gc);
- return MHD_YES;
- case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
- GNUNET_break (0);
- return MHD_NO;
- case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
- return MHD_NO;
- case ANASTASIS_AUTHORIZATION_RES_FINISHED:
- return return_key_share (&gc->truth_uuid,
- gc->connection);
- }
- GNUNET_break (0);
- return MHD_NO;
-}
-
-
-MHD_RESULT
-AH_handler_truth_get (
- struct MHD_Connection *connection,
- const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
- struct TM_HandlerContext *hc)
-{
- struct GetContext *gc = hc->ctx;
- void *encrypted_truth;
- size_t encrypted_truth_size;
- void *decrypted_truth;
- size_t decrypted_truth_size;
- char *truth_mime = NULL;
- bool is_question;
-
- if (NULL == gc)
- {
- /* Fresh request, do initial setup */
- gc = GNUNET_new (struct GetContext);
- gc->hc = hc;
- hc->ctx = gc;
- gc->connection = connection;
- gc->truth_uuid = *truth_uuid;
- gc->hc->cc = &request_done;
- {
- const char *pay_id;
-
- pay_id = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
- if (NULL != pay_id)
- {
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (
- pay_id,
- strlen (pay_id),
- &gc->payment_identifier,
- sizeof (struct ANASTASIS_PaymentSecretP)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
- }
- gc->payment_identifier_provided = true;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Client provided payment identifier `%s'\n",
- pay_id);
- }
- }
-
- {
- /* check if header contains Truth-Decryption-Key */
- const char *tdk;
-
- tdk = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- ANASTASIS_HTTP_HEADER_TRUTH_DECRYPTION_KEY);
- if (NULL == tdk)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MISSING,
- ANASTASIS_HTTP_HEADER_TRUTH_DECRYPTION_KEY);
- }
-
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (
- tdk,
- strlen (tdk),
- &gc->truth_key,
- sizeof (struct ANASTASIS_CRYPTO_TruthKeyP)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- ANASTASIS_HTTP_HEADER_TRUTH_DECRYPTION_KEY);
- }
- }
-
- {
- const char *challenge_response_s;
-
- challenge_response_s = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "response");
- if ( (NULL != challenge_response_s) &&
- (GNUNET_OK !=
- GNUNET_CRYPTO_hash_from_string (challenge_response_s,
- &gc->challenge_response)) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "response");
- }
- gc->have_response = (NULL != challenge_response_s);
- }
-
- {
- const char *long_poll_timeout_ms;
-
- long_poll_timeout_ms = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "timeout_ms");
- if (NULL != long_poll_timeout_ms)
- {
- unsigned int timeout;
- char dummy;
-
- if (1 != sscanf (long_poll_timeout_ms,
- "%u%c",
- &timeout,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "timeout_ms (must be non-negative number)");
- }
- gc->timeout
- = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
- GNUNET_TIME_UNIT_MILLISECONDS,
- timeout));
- }
- else
- {
- gc->timeout = GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_UNIT_SECONDS);
- }
- }
- } /* end of first-time initialization (if NULL == gc) */
- else
- {
- /* might have been woken up by authorization plugin,
- so clear the flag. MDH called us, so we are
- clearly no longer suspended */
- gc->suspended = false;
- if (NULL != gc->resp)
- {
- MHD_RESULT ret;
-
- /* We generated a response asynchronously, queue that */
- ret = MHD_queue_response (connection,
- gc->response_code,
- gc->resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (gc->resp);
- gc->resp = NULL;
- return ret;
- }
- if (NULL != gc->as)
- {
- /* Authorization process is "running", check what is going on */
- GNUNET_assert (NULL != gc->authorization);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Continuing with running the authorization process\n");
- GNUNET_assert (! gc->suspended);
- return run_authorization_process (connection,
- gc);
-
- }
- /* We get here if the async check for payment said this request
- was indeed paid! */
- }
-
- {
- /* load encrypted truth from DB */
- enum GNUNET_DB_QueryStatus qs;
- char *method;
-
- qs = db->get_escrow_challenge (db->cls,
- &gc->truth_uuid,
- &encrypted_truth,
- &encrypted_truth_size,
- &truth_mime,
- &method);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get escrow challenge");
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_ANASTASIS_TRUTH_UNKNOWN,
- NULL);
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- break;
- }
- is_question = (0 == strcmp ("question",
- method));
- if (! is_question)
- {
- gc->authorization
- = ANASTASIS_authorization_plugin_load (method,
- db,
- AH_cfg);
- if (NULL == gc->authorization)
- {
- MHD_RESULT ret;
-
- ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED,
- method);
- GNUNET_free (encrypted_truth);
- GNUNET_free (truth_mime);
- GNUNET_free (method);
- return ret;
- }
- gc->challenge_cost = gc->authorization->cost;
- }
- else
- {
- gc->challenge_cost = AH_question_cost;
- }
- GNUNET_free (method);
- }
-
- if ( (is_question) ||
- (! gc->authorization->payment_plugin_managed) )
- {
- if (! TALER_amount_is_zero (&gc->challenge_cost))
- {
- /* Check database to see if the transaction is paid for */
- enum GNUNET_DB_QueryStatus qs;
- bool paid;
-
- if (! gc->payment_identifier_provided)
- {
- GNUNET_free (truth_mime);
- GNUNET_free (encrypted_truth);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Beginning payment, client did not provide payment identifier\n");
- return begin_payment (gc);
- }
- qs = db->check_challenge_payment (db->cls,
- &gc->payment_identifier,
- &gc->truth_uuid,
- &paid);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- GNUNET_free (truth_mime);
- GNUNET_free (encrypted_truth);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "check challenge payment");
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* Create fresh payment identifier (cannot trust client) */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Client-provided payment identifier is unknown.\n");
- GNUNET_free (truth_mime);
- GNUNET_free (encrypted_truth);
- return begin_payment (gc);
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- if (! paid)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Payment identifier known. Checking payment with client's payment identifier\n");
- GNUNET_free (truth_mime);
- GNUNET_free (encrypted_truth);
- return begin_payment (gc);
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Payment confirmed\n");
- break;
- }
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request is free of charge\n");
- }
- }
-
- /* We've been paid, now validate response */
- {
- /* decrypt encrypted_truth */
- ANASTASIS_CRYPTO_truth_decrypt (&gc->truth_key,
- encrypted_truth,
- encrypted_truth_size,
- &decrypted_truth,
- &decrypted_truth_size);
- GNUNET_free (encrypted_truth);
- }
- if (NULL == decrypted_truth)
- {
- GNUNET_free (truth_mime);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_EXPECTATION_FAILED,
- TALER_EC_ANASTASIS_TRUTH_DECRYPTION_FAILED,
- NULL);
- }
-
- /* Special case for secure question: we do not generate a numeric challenge,
- but check that the hash matches */
- if (is_question)
- {
- MHD_RESULT ret;
-
- ret = handle_security_question (gc,
- decrypted_truth,
- decrypted_truth_size);
- GNUNET_free (truth_mime);
- GNUNET_free (decrypted_truth);
- return ret;
- }
-
- /* Not security question, check for answer in DB */
- if (gc->have_response)
- {
- enum ANASTASIS_DB_CodeStatus cs;
- bool satisfied;
- uint64_t code;
-
- GNUNET_free (truth_mime);
- if (gc->authorization->user_provided_code)
- {
- MHD_RESULT res;
-
- res = direct_validation (gc,
- decrypted_truth,
- decrypted_truth_size);
- GNUNET_free (decrypted_truth);
- return res;
- }
-
- /* random code, check against database */
- cs = db->verify_challenge_code (db->cls,
- &gc->truth_uuid,
- &gc->challenge_response,
- &code,
- &satisfied);
- switch (cs)
- {
- case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Provided response does not match our stored challenge\n");
- GNUNET_free (decrypted_truth);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
- NULL);
- case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
- case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- GNUNET_free (decrypted_truth);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "verify_challenge_code");
- case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Response code unknown (possibly expired). Testing if we may provide a new one.\n");
- gc->have_response = false;
- break;
- case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Response code valid (%s)\n",
- satisfied ? "satisfied" : "unsatisfied");
- if (satisfied)
- {
- GNUNET_free (decrypted_truth);
- return return_key_share (&gc->truth_uuid,
- connection);
- }
- /* continue with authorization plugin below */
- gc->code = code;
- break;
- default:
- GNUNET_break (0);
- return MHD_NO;
- }
- }
- if (! gc->have_response)
- {
- /* Not security question and no answer: use plugin to check if
- decrypted truth is a valid challenge! */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No challenge provided, creating fresh challenge\n");
- {
- enum GNUNET_GenericReturnValue ret;
-
- ret = gc->authorization->validate (gc->authorization->cls,
- connection,
- truth_mime,
- decrypted_truth,
- decrypted_truth_size);
- GNUNET_free (truth_mime);
- switch (ret)
- {
- case GNUNET_OK:
- /* data valid, continued below */
- break;
- case GNUNET_NO:
- /* data invalid, reply was queued */
- GNUNET_free (decrypted_truth);
- return MHD_YES;
- case GNUNET_SYSERR:
- /* data invalid, reply was NOT queued */
- GNUNET_free (decrypted_truth);
- return MHD_NO;
- }
- }
-
- /* Setup challenge and begin authorization process */
- {
- struct GNUNET_TIME_Timestamp transmission_date;
- enum GNUNET_DB_QueryStatus qs;
-
- qs = db->create_challenge_code (db->cls,
- &gc->truth_uuid,
- gc->authorization->code_rotation_period,
- gc->authorization->code_validity_period,
- gc->authorization->retry_counter,
- &transmission_date,
- &gc->code);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- GNUNET_free (decrypted_truth);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "create_challenge_code");
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* 0 == retry_counter of existing challenge => rate limit exceeded */
- GNUNET_free (decrypted_truth);
- return reply_rate_limited (gc);
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* challenge code was stored successfully*/
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Created fresh challenge\n");
- break;
- }
-
- if (GNUNET_TIME_relative_cmp (
- GNUNET_TIME_absolute_get_duration (
- transmission_date.abs_time),
- <,
- gc->authorization->code_retransmission_frequency) )
- {
- /* Too early for a retransmission! */
- GNUNET_free (decrypted_truth);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_ALREADY_REPORTED,
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_ACTIVE,
- NULL);
- }
- }
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Beginning authorization process\n");
- gc->as = gc->authorization->start (gc->authorization->cls,
- &AH_trigger_daemon,
- NULL,
- &gc->truth_uuid,
- gc->code,
- decrypted_truth,
- decrypted_truth_size);
- GNUNET_free (decrypted_truth);
- if (NULL == gc->as)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
- NULL);
- }
- if (! gc->in_list)
- {
- gc->in_list = true;
- GNUNET_CONTAINER_DLL_insert (gc_head,
- gc_tail,
- gc);
- }
- GNUNET_assert (! gc->suspended);
- return run_authorization_process (connection,
- gc);
-}
diff --git a/src/backend/anastasis-httpd_truth.h b/src/backend/anastasis-httpd_truth.h
index d0851ba..a436394 100644
--- a/src/backend/anastasis-httpd_truth.h
+++ b/src/backend/anastasis-httpd_truth.h
@@ -1,6 +1,6 @@
/*
This file is part of Anastasis
- Copyright (C) 2014, 2015, 2016, 2021 Anastasis SARL
+ Copyright (C) 2020-2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -24,14 +24,6 @@
#define ANASTASIS_HTTPD_TRUTH_H
#include <microhttpd.h>
-
-/**
- * Prepare all active GET truth requests for system shutdown.
- */
-void
-AH_truth_shutdown (void);
-
-
/**
* Prepare all active POST truth solve requests for system shutdown.
*/
@@ -53,21 +45,6 @@ AH_truth_upload_shutdown (void);
/**
- * Handle a GET to /truth/$UUID
- *
- * @param[in,out] connection the MHD connection to handle
- * @param truth_uuid the truth UUID
- * @param[in,out] hc connection context
- * @return MHD result code
- */
-MHD_RESULT
-AH_handler_truth_get (
- struct MHD_Connection *connection,
- const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
- struct TM_HandlerContext *hc);
-
-
-/**
* Handle a POST to /truth/$UUID.
*
* @param[in,out] connection the MHD connection to handle
diff --git a/src/include/anastasis.h b/src/include/anastasis.h
index 92c0745..90f3f5d 100644
--- a/src/include/anastasis.h
+++ b/src/include/anastasis.h
@@ -97,61 +97,38 @@ ANASTASIS_challenge_get_details (struct ANASTASIS_Challenge *challenge);
/**
* Possible outcomes of trying to start a challenge operation.
*/
-enum ANASTASIS_ChallengeStatus
+enum ANASTASIS_ChallengeStartStatus
{
/**
- * The challenge has been solved.
- */
- ANASTASIS_CHALLENGE_STATUS_SOLVED,
-
- /**
- * Instructions for how to solve the challenge are provided. Also
- * used if the answer we provided was wrong (or if no answer was
- * provided, but one is needed).
- */
- ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS,
-
- /**
- * A redirection URL needed to solve the challenge is provided. Also
- * used if the answer we provided was wrong (or if no answer was
- * provided, but one is needed).
+ * We encountered an error talking to the Anastasis service.
*/
- ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION,
+ ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE,
/**
* Payment is required before the challenge can be answered.
*/
- ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED,
-
- /**
- * We encountered an error talking to the Anastasis service.
- */
- ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE,
+ ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED,
/**
* The server does not know this truth.
*/
- ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN,
+ ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN,
/**
- * The rate limit for solving the challenge was exceeded.
+ * A filename with the TAN has been provided.
*/
- ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED,
+ ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED,
/**
- * The user did not satisfy the (external) authentication
- * challenge in time. The request should be repeated
- * later and may then succeed.
+ * A TAN has been send, address hint is provided.
*/
- ANASTASIS_CHALLENGE_STATUS_AUTH_TIMEOUT,
+ ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED,
/**
- * Plugin-specific ("external") instructions for how to solve the
- * challenge are provided.
+ * Wire transfer required, banking details provided.
*/
- ANASTASIS_CHALLENGE_STATUS_EXTERNAL_INSTRUCTIONS
-
+ ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED
};
@@ -161,10 +138,21 @@ enum ANASTASIS_ChallengeStatus
*/
struct ANASTASIS_ChallengeStartResponse
{
+
+ /**
+ * HTTP status returned by the server.
+ */
+ unsigned int http_status;
+
+ /**
+ * Taler-specific error code.
+ */
+ enum TALER_ErrorCode ec;
+
/**
* What is our status on satisfying this challenge. Determines @e details.
*/
- enum ANASTASIS_ChallengeStatus cs;
+ enum ANASTASIS_ChallengeStartStatus cs;
/**
* Which challenge is this about?
@@ -179,36 +167,159 @@ struct ANASTASIS_ChallengeStartResponse
/**
* Challenge details provided if
- * @e cs is #ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS
+ * @e cs is #ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED.
+ */
+ const char *tan_filename;
+
+ /**
+ * Challenge details provided if
+ * @e cs is #ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED.
+ */
+ const char *tan_address_hint;
+
+ /**
+ * Challenge details provided if
+ * @e cs is #ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED.
+ */
+ struct ANASTASIS_WireFundsDetails bank_transfer_required;
+
+ /**
+ * Response with instructions for how to pay, if
+ * @e cs is #ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED.
*/
struct
{
/**
- * Response with server-side instructions for the user.
+ * "taler://pay" URI with details how to pay for the challenge.
*/
- const void *body;
+ const char *taler_pay_uri;
/**
- * Mime type of the data in @e body.
+ * Payment secret from @e taler_pay_uri.
*/
- const char *content_type;
+ struct ANASTASIS_PaymentSecretP payment_secret;
- /**
- * Number of bytes in @e body
- */
- size_t body_size;
+ } payment_required;
+
+ } details;
+};
- /**
- * HTTP status returned by the server. #MHD_HTTP_ALREADY_REPORTED
- * if the server did already send the challenge to the user,
- * #MHD_HTTP_FORBIDDEN if the answer was wrong (or missing).
- */
- unsigned int http_status;
- } open_challenge;
+
+/**
+ * Defines a callback for the response status for a challenge start
+ * operation.
+ *
+ * @param cls closure
+ * @param csr response details
+ */
+typedef void
+(*ANASTASIS_ChallengeStartFeedback)(
+ void *cls,
+ const struct ANASTASIS_ChallengeStartResponse *csr);
+
+
+/**
+ * User starts a challenge which reponds out of bounds (E-Mail, SMS,
+ * Postal..) If the challenge is zero cost, the challenge
+ * instructions will be sent to the client. If the challenge needs
+ * payment a payment link is sent to the client. After payment the
+ * challenge start method has to be called again.
+ *
+ * @param c reference to the escrow challenge which is started
+ * @param psp payment secret, NULL if no payment was yet made
+ * @param af reference to the answerfeedback which is passed back to the user
+ * @param af_cls closure for @a af
+ * @return #GNUNET_OK if the challenge was successfully started
+ */
+enum GNUNET_GenericReturnValue
+ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
+ const struct ANASTASIS_PaymentSecretP *psp,
+ ANASTASIS_ChallengeStartFeedback af,
+ void *af_cls);
+
+
+/**
+ * Possible outcomes of trying to start a challenge operation.
+ */
+enum ANASTASIS_ChallengeAnswerStatus
+{
+
+ /**
+ * The challenge has been solved.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED,
+
+ /**
+ * Payment is required before the challenge can be answered.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED,
+
+ /**
+ * We encountered an error talking to the Anastasis service.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE,
+
+ /**
+ * The server does not know this truth.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN,
+
+ /**
+ * The answer was wrong.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER,
+
+ /**
+ * The rate limit for solving the challenge was exceeded.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED,
+
+ /**
+ * The user did not satisfy the (external) authentication
+ * challenge in time. The request should be repeated
+ * later and may then succeed.
+ */
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_AUTH_TIMEOUT
+
+
+};
+
+
+/**
+ * Response from an #ANASTASIS_challenge_start() operation.
+ */
+struct ANASTASIS_ChallengeAnswerResponse
+{
+
+ /**
+ * HTTP status returned by the server.
+ */
+ unsigned int http_status;
+
+ /**
+ * Taler-specific error code.
+ */
+ enum TALER_ErrorCode ec;
+
+ /**
+ * What is our status on satisfying this challenge. Determines @e details.
+ */
+ enum ANASTASIS_ChallengeAnswerStatus cs;
+
+ /**
+ * Which challenge is this about?
+ */
+ struct ANASTASIS_Challenge *challenge;
+
+ /**
+ * Details depending on @e cs
+ */
+ union
+ {
/**
- * Details for #ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED.
+ * Details for #ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED.
*/
struct
{
@@ -227,20 +338,8 @@ struct ANASTASIS_ChallengeStartResponse
} rate_limit_exceeded;
/**
- * Response with details if
- * @e cs is #ANASTASIS_CHALLENGE_STATUS_EXTERNAL_INSTRUCTIONS.
- */
- const json_t *external_challenge;
-
- /**
- * Response with URL to redirect the user to, if
- * @e cs is #ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION.
- */
- const char *redirect_url;
-
- /**
* Response with instructions for how to pay, if
- * @e cs is #ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED.
+ * @e cs is #ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED.
*/
struct
{
@@ -257,26 +356,6 @@ struct ANASTASIS_ChallengeStartResponse
} payment_required;
-
- /**
- * Response with details about a server-side failure, if
- * @e cs is #ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE.
- */
- struct
- {
-
- /**
- * HTTP status returned by the server.
- */
- unsigned int http_status;
-
- /**
- * Taler-specific error code.
- */
- enum TALER_ErrorCode ec;
-
- } server_failure;
-
} details;
};
@@ -286,36 +365,12 @@ struct ANASTASIS_ChallengeStartResponse
* operation.
*
* @param cls closure
- * @param csr response details
+ * @param car response details
*/
typedef void
(*ANASTASIS_AnswerFeedback)(
void *cls,
- const struct ANASTASIS_ChallengeStartResponse *csr);
-
-
-/**
- * User starts a challenge which reponds out of bounds (E-Mail, SMS,
- * Postal..) If the challenge is zero cost, the challenge
- * instructions will be sent to the client. If the challenge needs
- * payment a payment link is sent to the client. After payment the
- * challenge start method has to be called again.
- *
- * @param c reference to the escrow challenge which is started
- * @param psp payment secret, NULL if no payment was yet made
- * @param timeout how long to wait for payment
- * @param hashed_answer answer to the challenge, NULL if we have none yet
- * @param af reference to the answerfeedback which is passed back to the user
- * @param af_cls closure for @a af
- * @return #GNUNET_OK if the challenge was successfully started
- */
-enum GNUNET_GenericReturnValue
-ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
- const struct ANASTASIS_PaymentSecretP *psp,
- struct GNUNET_TIME_Relative timeout,
- const struct GNUNET_HashCode *hashed_answer,
- ANASTASIS_AnswerFeedback af,
- void *af_cls);
+ const struct ANASTASIS_ChallengeAnswerResponse *car);
/**
@@ -328,8 +383,8 @@ ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
* @param psp information about payment made for the recovery
* @param timeout how long to wait for payment
* @param answer user input instruction defines which input is needed
- * @param af reference to the answerfeedback which is passed back to the user
- * @param af_cls closure for @a af
+ * @param csf function to call with the result
+ * @param csf_cls closure for @a csf
* @return #GNUNET_OK on success
*/
enum GNUNET_GenericReturnValue
@@ -337,8 +392,8 @@ ANASTASIS_challenge_answer (struct ANASTASIS_Challenge *c,
const struct ANASTASIS_PaymentSecretP *psp,
struct GNUNET_TIME_Relative timeout,
const char *answer,
- ANASTASIS_AnswerFeedback af,
- void *af_cls);
+ ANASTASIS_AnswerFeedback csf,
+ void *csf_cls);
/**
@@ -365,6 +420,30 @@ ANASTASIS_challenge_answer2 (struct ANASTASIS_Challenge *c,
/**
+ * User starts a challenge which reponds out of bounds (E-Mail, SMS,
+ * Postal..) If the challenge is zero cost, the challenge
+ * instructions will be sent to the client. If the challenge needs
+ * payment a payment link is sent to the client. After payment the
+ * challenge start method has to be called again.
+ *
+ * @param c reference to the escrow challenge which is started
+ * @param psp payment secret, NULL if no payment was yet made
+ * @param timeout how long to wait for payment
+ * @param hashed_answer answer to the challenge
+ * @param af reference to the answerfeedback which is passed back to the user
+ * @param af_cls closure for @a af
+ * @return #GNUNET_OK if the challenge was successfully started
+ */
+enum GNUNET_GenericReturnValue
+ANASTASIS_challenge_answer3 (struct ANASTASIS_Challenge *c,
+ const struct ANASTASIS_PaymentSecretP *psp,
+ struct GNUNET_TIME_Relative timeout,
+ const struct GNUNET_HashCode *hashed_answer,
+ ANASTASIS_AnswerFeedback af,
+ void *af_cls);
+
+
+/**
* Abort answering challenge.
*
* @param c reference to the escrow challenge which was started
diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h
index c21cde5..e88b7e0 100644
--- a/src/include/anastasis_service.h
+++ b/src/include/anastasis_service.h
@@ -517,258 +517,6 @@ ANASTASIS_policy_store_cancel (
/**
- * Operational status.
- */
-enum ANASTASIS_KeyShareDownloadStatus
-{
- /**
- * We got the encrypted key share.
- */
- ANASTASIS_KSD_SUCCESS = 0,
-
- /**
- * Payment is needed to proceed with the recovery.
- */
- ANASTASIS_KSD_PAYMENT_REQUIRED,
-
- /**
- * The provided answer was wrong or missing. Instructions for
- * getting a good answer may be provided.
- */
- ANASTASIS_KSD_INVALID_ANSWER,
-
- /**
- * To answer the challenge, the client should be redirected to
- * the given URL.
- */
- ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION,
-
- /**
- * The provider had an error.
- */
- ANASTASIS_KSD_SERVER_ERROR,
-
- /**
- * The provider claims we made an error.
- */
- ANASTASIS_KSD_CLIENT_FAILURE,
-
- /**
- * The provider does not know this truth.
- */
- ANASTASIS_KSD_TRUTH_UNKNOWN,
-
- /**
- * Too many attempts to solve the challenge were made in a short
- * time. Try again later.
- */
- ANASTASIS_KSD_RATE_LIMIT_EXCEEDED,
-
- /**
- * The user did not satisfy the (external)
- * authentication check until the request timeout
- * was reached. The client should try again later.
- */
- ANASTASIS_KSD_AUTHENTICATION_TIMEOUT,
-
- /**
- * The plugin provided external challenge instructions
- * that should be followed. They are method-specific.
- */
- ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS
-
-};
-
-
-/**
- * Detailed results from the successful download.
- */
-struct ANASTASIS_KeyShareDownloadDetails
-{
-
- /**
- * Operational status.
- */
- enum ANASTASIS_KeyShareDownloadStatus status;
-
- /**
- * Anastasis URL that returned the @e status.
- */
- const char *server_url;
-
- /**
- * Details depending on @e status.
- */
- union
- {
-
- /**
- * The encrypted key share (if @e status is #ANASTASIS_KSD_SUCCESS).
- */
- struct ANASTASIS_CRYPTO_EncryptedKeyShareP eks;
-
- /**
- * Response if the challenge still needs to be answered, and the
- * instructions are provided inline (no redirection).
- */
- struct
- {
-
- /**
- * HTTP status returned by the server. #MHD_HTTP_ALREADY_REPORTED
- * if the server did already send the challenge to the user,
- * #MHD_HTTP_FORBIDDEN if the answer was wrong (or missing).
- */
- unsigned int http_status;
-
- /**
- * Response with server-side reply containing instructions for the user
- */
- const char *body;
-
- /**
- * Content-type: mime type of @e body, NULL if server did not provide any.
- */
- const char *content_type;
-
- /**
- * Number of bytes in @e body.
- */
- size_t body_size;
-
- } open_challenge;
-
- /**
- * URL with instructions for the user to satisfy the challenge, if
- * @e status is #ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION.
- */
- const char *redirect_url;
-
- /**
- * Response with instructions for how to pay, if
- * @e status is #ANASTASIS_KSD_PAYMENT_REQUIRED.
- */
- struct
- {
-
- /**
- * "taler://pay" URL with details how to pay for the challenge.
- */
- const char *taler_pay_uri;
-
- /**
- * The order ID from @e taler_pay_uri.
- */
- struct ANASTASIS_PaymentSecretP payment_secret;
-
- } payment_required;
-
-
- struct
- {
-
- /**
- * How many requests are allowed at most per @e request_frequency?
- */
- uint32_t request_limit;
-
- /**
- * Frequency at which requests are allowed / new challenges are
- * created.
- */
- struct GNUNET_TIME_Relative request_frequency;
-
- } rate_limit_exceeded;
-
-
- /**
- * Response with details about a server-side failure, if
- * @e status is #ANASTASIS_KSD_SERVER_ERROR,
- * #ANASTASIS_KSD_CLIENT_FAILURE or #ANASTASIS_KSD_TRUTH_UNKNOWN.
- */
- struct
- {
-
- /**
- * HTTP status returned by the server.
- */
- unsigned int http_status;
-
- /**
- * Taler-specific error code.
- */
- enum TALER_ErrorCode ec;
-
- } server_failure;
-
- /**
- * External challenge instructions, if @e status is
- * #ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS.
- */
- const json_t *external_challenge;
-
- } details;
-};
-
-
-/**
- * Handle for a GET /truth operation.
- */
-struct ANASTASIS_KeyShareLookupOperation;
-
-
-/**
- * Callback to process a GET /truth request
- *
- * @param cls closure
- * @param http_status HTTP status code for this request
- * @param kdd details about the key share
- */
-typedef void
-(*ANASTASIS_KeyShareLookupCallback) (
- void *cls,
- const struct ANASTASIS_KeyShareDownloadDetails *kdd);
-
-
-/**
- * Does a GET /truth.
- *
- * @param ctx execution context
- * @param backend_url base URL of the merchant backend
- * @param truth_uuid identification of the Truth
- * @param truth_key Key used to Decrypt the Truth on the Server
- * @param payment_secret secret from the previously done payment NULL to trigger payment
- * @param timeout how long to wait for the payment, use
- * #GNUNET_TIME_UNIT_ZERO to let the server pick
- * @param hashed_answer hashed answer to the challenge
- * @param cb callback which will work the response gotten from the backend
- * @param cb_cls closure to pass to the callback
- * @return handle for this operation, NULL upon errors
- */
-struct ANASTASIS_KeyShareLookupOperation *
-ANASTASIS_keyshare_lookup (
- struct GNUNET_CURL_Context *ctx,
- const char *backend_url,
- const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
- const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key,
- const struct ANASTASIS_PaymentSecretP *payment_secret,
- struct GNUNET_TIME_Relative timeout,
- const struct GNUNET_HashCode *hashed_answer,
- ANASTASIS_KeyShareLookupCallback cb,
- void *cb_cls);
-
-
-/**
- * Cancel a GET /truth request.
- *
- * @param kslo cancel the key share lookup operation
- */
-void
-ANASTASIS_keyshare_lookup_cancel (
- struct ANASTASIS_KeyShareLookupOperation *kslo);
-
-
-/**
* Handle for a POST /truth operation.
*/
struct ANASTASIS_TruthStoreOperation;
@@ -836,6 +584,12 @@ enum ANASTASIS_ChallengeDetailType
{
/**
+ * A challenge TAN was written to a file.
+ * The name of the file is provided.
+ */
+ ANASTASIS_CS_FILE_WRITTEN,
+
+ /**
* A challenge TAN was sent to the customer.
* A hint may be provided as to the address used.
*/
@@ -851,6 +605,42 @@ enum ANASTASIS_ChallengeDetailType
/**
+ * This structure contains information about where to wire the funds
+ * to authenticate as well as a hint as to which bank account to send
+ * the funds from.
+ */
+struct ANASTASIS_WireFundsDetails
+{
+
+ /**
+ * Answer code expected.
+ */
+ uint64_t answer_code;
+
+ /**
+ * How much should be sent.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * IBAN where to send the funds.
+ */
+ const char *target_iban;
+
+ /**
+ * Name of the business receiving the funds.
+ */
+ const char *target_business_name;
+
+ /**
+ * Wire transfer subject to use.
+ */
+ const char *wire_transfer_subject;
+
+};
+
+
+/**
* Information returned for a POST /truth/$TID/challenge request.
*/
struct ANASTASIS_TruthChallengeDetails
@@ -894,6 +684,12 @@ struct ANASTASIS_TruthChallengeDetails
{
/**
+ * If @e cs is #ANASTASIS_CS_FILE_WRITTEN, this
+ * is the filename with the challenge code.
+ */
+ const char *challenge_filename;
+
+ /**
* If @e cs is #ANASTASIS_CS_TAN_SENT, this
* is human-readable information as to where
* the TAN was sent.
@@ -907,28 +703,7 @@ struct ANASTASIS_TruthChallengeDetails
* as a hint as to which bank account to send
* the funds from.
*/
- struct
- {
-
- /**
- * How much should be sent.
- */
- struct TALER_Amount amount;
-
- /**
- * payto:// URI with the target account number.
- */
- const char *target_payto;
-
- /**
- * Human-readable hint about which sender bank
- * account must be used.
- */
- const char *sender_hint;
-
- // FIXME: more? Wire transfer subject?
-
- } wire_funds;
+ struct ANASTASIS_WireFundsDetails wire_funds;
} details;
diff --git a/src/include/anastasis_testing_lib.h b/src/include/anastasis_testing_lib.h
index ba1b8a3..0066939 100644
--- a/src/include/anastasis_testing_lib.h
+++ b/src/include/anastasis_testing_lib.h
@@ -374,7 +374,28 @@ ANASTASIS_TESTING_cmd_truth_question (
/**
- * Make the "keyshare lookup" command.
+ * Make a "truth challenge" command.
+ *
+ * @param label command label
+ * @param anastasis_url base URL of the ANASTASIS serving
+ * the keyshare lookup request.
+ * @param answer (response to challenge)
+ * @param payment_ref reference to the payment request
+ * @param upload_ref reference to upload command
+ * @param http_status expected HTTP status
+ * @return the command
+ */
+struct TALER_TESTING_Command
+ANASTASIS_TESTING_cmd_truth_challenge (
+ const char *label,
+ const char *anastasis_url,
+ const char *payment_ref,
+ const char *upload_ref,
+ unsigned int http_status);
+
+
+/**
+ * Make a "truth solve" command.
*
* @param label command label
* @param anastasis_url base URL of the ANASTASIS serving
@@ -384,18 +405,18 @@ ANASTASIS_TESTING_cmd_truth_question (
* @param upload_ref reference to upload command
* @param lookup_mode 0 for security question, 1 for
* code-based
- * @param ksdd expected status
+ * @param http_status expected HTTP status
* @return the command
*/
struct TALER_TESTING_Command
-ANASTASIS_TESTING_cmd_keyshare_lookup (
+ANASTASIS_TESTING_cmd_truth_solve (
const char *label,
const char *anastasis_url,
const char *answer,
const char *payment_ref,
const char *upload_ref,
int lookup_mode,
- enum ANASTASIS_KeyShareDownloadStatus ksdd);
+ unsigned int http_status);
/**
@@ -633,7 +654,7 @@ ANASTASIS_TESTING_cmd_challenge_start (
const char *payment_ref,
const char *challenge_ref,
unsigned int challenge_index,
- enum ANASTASIS_ChallengeStatus expected_cs);
+ enum ANASTASIS_ChallengeStartStatus expected_cs);
/**
@@ -657,7 +678,7 @@ ANASTASIS_TESTING_cmd_challenge_answer (
unsigned int challenge_index,
const char *answer,
unsigned int mode,
- enum ANASTASIS_ChallengeStatus expected_cs);
+ enum ANASTASIS_ChallengeAnswerStatus expected_cs);
#endif
diff --git a/src/lib/anastasis_recovery.c b/src/lib/anastasis_recovery.c
index 9e5d1ca..a450392 100644
--- a/src/lib/anastasis_recovery.c
+++ b/src/lib/anastasis_recovery.c
@@ -63,16 +63,27 @@ struct ANASTASIS_Challenge
/**
* Callback which gives back the instructions and a status code of
- * the request to the user when answering a challenge was initiated.
+ * the request to the user when answering a challenge.
*/
ANASTASIS_AnswerFeedback af;
/**
- * Closure for the challenge callback
+ * Closure for @e af.
*/
void *af_cls;
/**
+ * Callback which gives back the instructions and a status code of
+ * the request to the user when initiating a challenge.
+ */
+ ANASTASIS_ChallengeStartFeedback csf;
+
+ /**
+ * Closure for @e csf.
+ */
+ void *csf_cls;
+
+ /**
* Defines the base URL of the Anastasis provider used for the challenge.
*/
char *url;
@@ -99,9 +110,14 @@ struct ANASTASIS_Challenge
struct ANASTASIS_Recovery *recovery;
/**
- * keyshare lookup operation
+ * Handle for the /truth/$TID/challenge request.
+ */
+ struct ANASTASIS_TruthChallengeOperation *tco;
+
+ /**
+ * Handle for the /truth/$TID/solve request.
*/
- struct ANASTASIS_KeyShareLookupOperation *kslo;
+ struct ANASTASIS_TruthSolveOperation *tso;
};
@@ -238,165 +254,137 @@ struct ANASTASIS_Recovery
/**
- * Function called with the results of a #ANASTASIS_keyshare_lookup().
+ * Function called with the results of a #ANASTASIS_challenge_start().
*
* @param cls closure
* @param dd details about the lookup operation
*/
static void
-keyshare_lookup_cb (void *cls,
- const struct ANASTASIS_KeyShareDownloadDetails *dd)
+truth_challenge_cb (void *cls,
+ const struct ANASTASIS_TruthChallengeDetails *tcd)
{
struct ANASTASIS_Challenge *c = cls;
- struct ANASTASIS_Recovery *recovery = c->recovery;
- struct ANASTASIS_CRYPTO_UserIdentifierP id;
- struct DecryptionPolicy *rdps;
-
- c->kslo = NULL;
- switch (dd->status)
+ struct ANASTASIS_ChallengeStartResponse csr = {
+ .challenge = c,
+ .ec = tcd->ec,
+ .http_status = tcd->http_status
+ };
+
+ c->tco = NULL;
+ switch (tcd->http_status)
{
- case ANASTASIS_KSD_SUCCESS:
- break;
- case ANASTASIS_KSD_PAYMENT_REQUIRED:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED,
- .challenge = c,
- .details.payment_required.taler_pay_uri
- = dd->details.payment_required.taler_pay_uri,
- .details.payment_required.payment_secret
- = dd->details.payment_required.payment_secret
- };
-
- c->af (c->af_cls,
- &csr);
- return;
- }
- case ANASTASIS_KSD_INVALID_ANSWER:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS,
- .challenge = c,
- .details.open_challenge.body
- = dd->details.open_challenge.body,
- .details.open_challenge.content_type
- = dd->details.open_challenge.content_type,
- .details.open_challenge.body_size
- = dd->details.open_challenge.body_size,
- .details.open_challenge.http_status
- = dd->details.open_challenge.http_status
- };
-
- c->af (c->af_cls,
- &csr);
- return;
- }
- case ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION,
- .challenge = c,
- .details.redirect_url
- = dd->details.redirect_url
- };
-
- c->af (c->af_cls,
- &csr);
- return;
- }
- case ANASTASIS_KSD_TRUTH_UNKNOWN:
+ case MHD_HTTP_OK:
+ switch (tcd->details.success.cs)
{
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN,
- .challenge = c
- };
-
- c->af (c->af_cls,
- &csr);
- return;
+ case ANASTASIS_CS_FILE_WRITTEN:
+ csr.cs = ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED;
+ csr.details.tan_filename
+ = tcd->details.success.details.challenge_filename;
+ break;
+ case ANASTASIS_CS_TAN_SENT:
+ csr.cs = ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED;
+ csr.details.tan_address_hint
+ = tcd->details.success.details.tan_address_hint;
+ break;
+ case ANASTASIS_CS_WIRE_FUNDS:
+ csr.cs = ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED;
+ csr.details.bank_transfer_required
+ = tcd->details.success.details.wire_funds;
+ break;
}
- case ANASTASIS_KSD_RATE_LIMIT_EXCEEDED:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED,
- .challenge = c,
- .details.rate_limit_exceeded.request_limit
- = dd->details.rate_limit_exceeded.request_limit,
- .details.rate_limit_exceeded.request_frequency
- = dd->details.rate_limit_exceeded.request_frequency
- };
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ csr.cs = ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED;
+ csr.details.payment_required.taler_pay_uri
+ = tcd->details.payment_required.payment_request;
+ csr.details.payment_required.payment_secret
+ = tcd->details.payment_required.ps;
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ csr.cs = ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN;
+ break;
+ default:
+ csr.cs = ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE;
+ break;
+ }
+ c->csf (c->csf_cls,
+ &csr);
+}
- c->af (c->af_cls,
- &csr);
- return;
- }
- case ANASTASIS_KSD_SERVER_ERROR:
- case ANASTASIS_KSD_CLIENT_FAILURE:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE,
- .challenge = c,
- .details.server_failure.ec
- = dd->details.server_failure.ec,
- .details.server_failure.http_status
- = dd->details.server_failure.http_status
- };
- c->af (c->af_cls,
- &csr);
- return;
- }
- case ANASTASIS_KSD_AUTHENTICATION_TIMEOUT:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_AUTH_TIMEOUT,
- .challenge = c,
- .details.server_failure.ec
- = dd->details.server_failure.ec,
- .details.server_failure.http_status
- = dd->details.server_failure.http_status
- };
+/**
+ * Function called with the results of a #ANASTASIS_truth_solve().
+ *
+ * @param cls closure
+ * @param tsr details about the solution response
+ */
+static void
+truth_solve_cb (void *cls,
+ const struct ANASTASIS_TruthSolveReply *tsr)
+{
+ struct ANASTASIS_Challenge *c = cls;
+ struct ANASTASIS_Recovery *recovery = c->recovery;
+ struct ANASTASIS_CRYPTO_UserIdentifierP id;
+ struct DecryptionPolicy *rdps;
+ struct ANASTASIS_ChallengeAnswerResponse csr = {
+ .challenge = c,
+ .ec = tsr->ec,
+ .http_status = tsr->http_status
+ };
- c->ci.async = true;
- c->af (c->af_cls,
- &csr);
- return;
- }
- case ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS:
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_EXTERNAL_INSTRUCTIONS,
- .challenge = c,
- .details.external_challenge = dd->details.external_challenge
- };
- c->af (c->af_cls,
- &csr);
- return;
- }
+ c->tso = NULL;
+ switch (tsr->http_status)
+ {
+ case MHD_HTTP_OK:
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ csr.cs = ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED;
+ csr.details.payment_required.taler_pay_uri
+ = tsr->details.payment_required.payment_request;
+ csr.details.payment_required.payment_secret
+ = tsr->details.payment_required.ps;
+ c->af (c->af_cls,
+ &csr);
+ return;
+ case MHD_HTTP_FORBIDDEN:
+ csr.cs = ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER;
+ c->af (c->af_cls,
+ &csr);
+ return;
+ case MHD_HTTP_NOT_FOUND:
+ csr.cs = ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN;
+ c->af (c->af_cls,
+ &csr);
+ return;
+ case MHD_HTTP_TOO_MANY_REQUESTS:
+ csr.cs = ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED;
+ csr.details.rate_limit_exceeded.request_limit
+ = tsr->details.too_many_requests.request_limit;
+ csr.details.rate_limit_exceeded.request_frequency
+ = tsr->details.too_many_requests.request_frequency;
+ c->af (c->af_cls,
+ &csr);
+ return;
+ default:
+ csr.cs = ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE;
+ c->af (c->af_cls,
+ &csr);
+ return;
}
- GNUNET_assert (NULL != dd);
ANASTASIS_CRYPTO_user_identifier_derive (recovery->id_data,
&c->provider_salt,
&id);
- ANASTASIS_CRYPTO_keyshare_decrypt (&dd->details.eks,
+ ANASTASIS_CRYPTO_keyshare_decrypt (&tsr->details.success.eks,
&id,
c->answer,
&c->key_share);
recovery->solved_challenges[recovery->solved_challenge_pos++] = c;
-
- {
- struct ANASTASIS_ChallengeStartResponse csr = {
- .cs = ANASTASIS_CHALLENGE_STATUS_SOLVED,
- .challenge = c
- };
-
- c->ci.solved = true;
- c->af (c->af_cls,
- &csr);
- }
-
+ c->ci.solved = true;
+ csr.cs = ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED;
+ c->af (c->af_cls,
+ &csr);
/* Check if there is a policy for which all challenges have
been satisfied, if so, store it in 'rdps'. */
@@ -477,33 +465,67 @@ ANASTASIS_challenge_get_details (struct ANASTASIS_Challenge *challenge)
enum GNUNET_GenericReturnValue
ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
const struct ANASTASIS_PaymentSecretP *psp,
- struct GNUNET_TIME_Relative timeout,
- const struct GNUNET_HashCode *hashed_answer,
- ANASTASIS_AnswerFeedback af,
- void *af_cls)
+ ANASTASIS_ChallengeStartFeedback csf,
+ void *csf_cls)
{
if (c->ci.solved)
{
GNUNET_break (0);
return GNUNET_NO; /* already solved */
}
- if (NULL != c->kslo)
+ if (NULL != c->tco)
+ {
+ GNUNET_break (0);
+ return GNUNET_NO; /* already solving */
+ }
+ c->csf = csf;
+ c->csf_cls = csf_cls;
+ c->tco = ANASTASIS_truth_challenge (c->recovery->ctx,
+ c->url,
+ &c->ci.uuid,
+ &c->truth_key,
+ psp,
+ &truth_challenge_cb,
+ c);
+ if (NULL == c->tco)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+ANASTASIS_challenge_answer3 (struct ANASTASIS_Challenge *c,
+ const struct ANASTASIS_PaymentSecretP *psp,
+ struct GNUNET_TIME_Relative timeout,
+ const struct GNUNET_HashCode *hashed_answer,
+ ANASTASIS_AnswerFeedback af,
+ void *af_cls)
+{
+ if (c->ci.solved)
+ {
+ GNUNET_break (0);
+ return GNUNET_NO; /* already solved */
+ }
+ if (NULL != c->tso)
{
GNUNET_break (0);
return GNUNET_NO; /* already solving */
}
c->af = af;
c->af_cls = af_cls;
- c->kslo = ANASTASIS_keyshare_lookup (c->recovery->ctx,
- c->url,
- &c->ci.uuid,
- &c->truth_key,
- psp,
- timeout,
- hashed_answer,
- &keyshare_lookup_cb,
- c);
- if (NULL == c->kslo)
+ c->tso = ANASTASIS_truth_solve (c->recovery->ctx,
+ c->url,
+ &c->ci.uuid,
+ &c->truth_key,
+ psp,
+ timeout,
+ hashed_answer,
+ &truth_solve_cb,
+ c);
+ if (NULL == c->tso)
{
GNUNET_break (0);
return GNUNET_SYSERR;
@@ -529,12 +551,12 @@ ANASTASIS_challenge_answer (
&c->ci.uuid,
&c->salt,
&hashed_answer);
- return ANASTASIS_challenge_start (c,
- psp,
- timeout,
- &hashed_answer,
- af,
- af_cls);
+ return ANASTASIS_challenge_answer3 (c,
+ psp,
+ timeout,
+ &hashed_answer,
+ af,
+ af_cls);
}
@@ -550,25 +572,28 @@ ANASTASIS_challenge_answer2 (struct ANASTASIS_Challenge *c,
ANASTASIS_hash_answer (answer,
&answer_s);
- return ANASTASIS_challenge_start (c,
- psp,
- timeout,
- &answer_s,
- af,
- af_cls);
+ return ANASTASIS_challenge_answer3 (c,
+ psp,
+ timeout,
+ &answer_s,
+ af,
+ af_cls);
}
void
ANASTASIS_challenge_abort (struct ANASTASIS_Challenge *c)
{
- if (NULL == c->kslo)
+ if (NULL != c->tso)
{
- GNUNET_break (0);
- return;
+ ANASTASIS_truth_solve_cancel (c->tso);
+ c->tso = NULL;
+ }
+ if (NULL != c->tco)
+ {
+ ANASTASIS_truth_challenge_cancel (c->tco);
+ c->tco = NULL;
}
- ANASTASIS_keyshare_lookup_cancel (c->kslo);
- c->kslo = NULL;
c->af = NULL;
c->af_cls = NULL;
}
@@ -1461,10 +1486,10 @@ ANASTASIS_recovery_abort (struct ANASTASIS_Recovery *r)
{
struct ANASTASIS_Challenge *cs = &r->cs[i];
- if (NULL != cs->kslo)
+ if (NULL != cs->tso)
{
- ANASTASIS_keyshare_lookup_cancel (cs->kslo);
- cs->kslo = NULL;
+ ANASTASIS_truth_solve_cancel (cs->tso);
+ cs->tso = NULL;
}
GNUNET_free (cs->url);
GNUNET_free (cs->type);
diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c
index 2be963f..5de278c 100644
--- a/src/reducer/anastasis_api_recovery_redux.c
+++ b/src/reducer/anastasis_api_recovery_redux.c
@@ -504,7 +504,7 @@ find_challenge_in_cs (json_t *state,
* @param csr response details
*/
static void
-answer_feedback_cb (
+start_feedback_cb (
void *cls,
const struct ANASTASIS_ChallengeStartResponse *csr)
{
@@ -533,7 +533,246 @@ answer_feedback_cb (
}
switch (csr->cs)
{
- case ANASTASIS_CHALLENGE_STATUS_SOLVED:
+ case ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED:
+ {
+ json_t *instructions;
+
+ instructions = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "hint"),
+ GNUNET_JSON_pack_string ("hint",
+ csr->details.tan_filename),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ (json_int_t) csr->http_status));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ instructions));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED:
+ {
+ json_t *instructions;
+
+ instructions = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "hint"),
+ GNUNET_JSON_pack_string ("hint",
+ csr->details.tan_address_hint),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ (json_int_t) csr->http_status));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ instructions));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED:
+ {
+ json_t *pay;
+
+ pay = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "payment"),
+ GNUNET_JSON_pack_string (
+ "taler_pay_uri",
+ csr->details.payment_required.taler_pay_uri),
+ GNUNET_JSON_pack_string ("provider",
+ cd->provider_url),
+ GNUNET_JSON_pack_data_auto (
+ "payment_secret",
+ &csr->details.payment_required.payment_secret));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ pay));
+ }
+ /* Remember payment secret for later (once application claims it paid) */
+ {
+ json_t *challenge = find_challenge_in_ri (sctx->state,
+ &cd->uuid);
+
+ GNUNET_assert (NULL != challenge);
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ challenge,
+ "payment_secret",
+ GNUNET_JSON_from_data_auto (
+ &csr->details.payment_required.payment_secret)));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE:
+ {
+ json_t *err;
+
+ err = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "server-failure"),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ csr->http_status),
+ GNUNET_JSON_pack_uint64 ("error_code",
+ csr->ec));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ err));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
+ sctx->cb (sctx->cb_cls,
+ csr->ec,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN:
+ {
+ json_t *err;
+
+ err = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "truth-unknown"),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ csr->http_status),
+ GNUNET_JSON_pack_uint64 ("error_code",
+ TALER_EC_ANASTASIS_TRUTH_UNKNOWN));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ err));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_ANASTASIS_TRUTH_UNKNOWN,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED:
+ {
+ json_t *reply;
+ json_t *c;
+
+ c = find_challenge_in_cs (sctx->state,
+ &cd->uuid);
+ if (NULL == c)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
+ sctx_free (sctx);
+ return;
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (c,
+ "async",
+ json_true ()));
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (
+ c,
+ "answer-pin",
+ json_integer (
+ csr->details.bank_transfer_required.answer_code)));
+ reply = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "iban-instructions"),
+ GNUNET_JSON_pack_string (
+ "target_iban",
+ csr->details.bank_transfer_required.target_iban),
+ GNUNET_JSON_pack_string (
+ "target_business_name",
+ csr->details.bank_transfer_required.target_business_name),
+ GNUNET_JSON_pack_string (
+ "wire_transfer_subject",
+ csr->details.bank_transfer_required.wire_transfer_subject),
+ TALER_JSON_pack_amount (
+ "challenge_amount",
+ &csr->details.bank_transfer_required.amount));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ reply));
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (sctx->state,
+ "selected_challenge_uuid",
+ GNUNET_JSON_from_data_auto (
+ &cd->uuid)));
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ }
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
+ sctx_free (sctx);
+}
+
+
+/**
+ * Defines a callback for the response status for a challenge answer
+ * operation.
+ *
+ * @param cls a `struct SelectChallengeContext *`
+ * @param csr response details
+ */
+static void
+answer_feedback_cb (
+ void *cls,
+ const struct ANASTASIS_ChallengeAnswerResponse *csr)
+{
+ struct SelectChallengeContext *sctx = cls;
+ const struct ANASTASIS_ChallengeDetails *cd;
+ char uuid[sizeof (cd->uuid) * 2];
+ char *end;
+ json_t *feedback;
+
+ cd = ANASTASIS_challenge_get_details (csr->challenge);
+ end = GNUNET_STRINGS_data_to_string (&cd->uuid,
+ sizeof (cd->uuid),
+ uuid,
+ sizeof (uuid));
+ GNUNET_assert (NULL != end);
+ *end = '\0';
+ feedback = json_object_get (sctx->state,
+ "challenge_feedback");
+ if (NULL == feedback)
+ {
+ feedback = json_object ();
+ GNUNET_assert (0 ==
+ json_object_set_new (sctx->state,
+ "challenge_feedback",
+ feedback));
+ }
+ switch (csr->cs)
+ {
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED:
{
json_t *rd;
@@ -570,77 +809,17 @@ answer_feedback_cb (
sctx->delayed_report = GNUNET_SCHEDULER_add_now (&report_solved,
sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER:
{
json_t *instructions;
- const char *mime;
-
- mime = csr->details.open_challenge.content_type;
- if (NULL != mime)
- {
- if ( (0 == strcasecmp (mime,
- "text/plain")) ||
- (0 == strcasecmp (mime,
- "text/utf8")) )
- {
- char *s = GNUNET_strndup (csr->details.open_challenge.body,
- csr->details.open_challenge.body_size);
-
- instructions = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "hint"),
- GNUNET_JSON_pack_string ("hint",
- s),
- GNUNET_JSON_pack_uint64 ("http_status",
- (json_int_t) csr->details.open_challenge.
- http_status));
- GNUNET_free (s);
- }
- else if (0 == strcasecmp (mime,
- "application/json"))
- {
- json_t *body;
- body = json_loadb (csr->details.open_challenge.body,
- csr->details.open_challenge.body_size,
- JSON_REJECT_DUPLICATES,
- NULL);
- if (NULL == body)
- {
- GNUNET_break_op (0);
- mime = NULL;
- }
- else
- {
- instructions = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "details"),
- GNUNET_JSON_pack_object_steal ("details",
- body),
- GNUNET_JSON_pack_uint64 ("http_status",
- csr->details.open_challenge.http_status));
- }
- }
- else
- {
- /* unexpected / unsupported mime type */
- mime = NULL;
- }
- }
- if (NULL == mime)
- {
- instructions = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "body"),
- GNUNET_JSON_pack_data_varsize ("body",
- csr->details.open_challenge.body,
- csr->details.open_challenge.body_size),
- GNUNET_JSON_pack_uint64 ("http_status",
- csr->details.open_challenge.http_status),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("mime_type",
- mime)));
- }
+ instructions = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("status",
+ "incorrect-answer"),
+ GNUNET_JSON_pack_uint64 ("error_code",
+ csr->ec),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ csr->http_status));
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
@@ -653,35 +832,13 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION:
- {
- json_t *redir;
-
- redir = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "redirect"),
- GNUNET_JSON_pack_string ("redirect_url",
- csr->details.redirect_url));
- GNUNET_assert (NULL != redir);
- GNUNET_assert (0 ==
- json_object_set_new (feedback,
- uuid,
- redir));
- }
- set_state (sctx->state,
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
- sctx->cb (sctx->cb_cls,
- TALER_EC_NONE,
- sctx->state);
- sctx_free (sctx);
- return;
- case ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED:
{
json_t *pay;
pay = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
- "payment"),
+ "payment-required"),
GNUNET_JSON_pack_string ("taler_pay_uri",
csr->details.payment_required.
taler_pay_uri),
@@ -715,7 +872,7 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE:
{
json_t *err;
@@ -723,10 +880,9 @@ answer_feedback_cb (
GNUNET_JSON_pack_string ("state",
"server-failure"),
GNUNET_JSON_pack_uint64 ("http_status",
- csr->details.server_failure.
- http_status),
+ csr->http_status),
GNUNET_JSON_pack_uint64 ("error_code",
- csr->details.server_failure.ec));
+ csr->ec));
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
@@ -735,11 +891,11 @@ answer_feedback_cb (
set_state (sctx->state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
sctx->cb (sctx->cb_cls,
- csr->details.server_failure.ec,
+ csr->ec,
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN:
{
json_t *err;
@@ -760,7 +916,7 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED:
{
json_t *err;
@@ -789,7 +945,8 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_AUTH_TIMEOUT:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_AUTH_TIMEOUT:
+ // FIXME: check if this status code is even properly generated!
{
json_t *err;
@@ -811,95 +968,6 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
-
- case ANASTASIS_CHALLENGE_STATUS_EXTERNAL_INSTRUCTIONS:
- {
- const json_t *body = csr->details.external_challenge;
- const char *method;
- json_t *details;
- bool is_async = false;
- uint64_t code = 0;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("method",
- &method),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("async",
- &is_async)),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint64 ("answer_code",
- &code)),
- GNUNET_JSON_spec_json ("details",
- &details),
- GNUNET_JSON_spec_end ()
- };
- json_t *reply;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (body,
- spec,
- NULL, NULL))
- {
- json_t *err;
-
- GNUNET_break_op (0);
- err = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "server-failure"),
- GNUNET_JSON_pack_uint64 ("error_code",
- TALER_EC_GENERIC_REPLY_MALFORMED));
- GNUNET_assert (0 ==
- json_object_set_new (feedback,
- uuid,
- err));
- return;
- }
- if (is_async)
- {
- json_t *c = find_challenge_in_cs (sctx->state,
- &cd->uuid);
-
- if (NULL == c)
- {
- GNUNET_break (0);
- ANASTASIS_redux_fail_ (sctx->cb,
- sctx->cb_cls,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
- sctx_free (sctx);
- return;
- }
- GNUNET_assert (0 ==
- json_object_set_new (c,
- "async",
- json_true ()));
- GNUNET_assert (0 ==
- json_object_set_new (c,
- "answer-pin",
- json_integer (code)));
- }
- reply = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "external-instructions"),
- GNUNET_JSON_pack_string ("method",
- method),
- GNUNET_JSON_pack_object_incref ("details",
- details));
- GNUNET_JSON_parse_free (spec);
- GNUNET_assert (0 ==
- json_object_set_new (feedback,
- uuid,
- reply));
- }
- json_object_set_new (sctx->state,
- "selected_challenge_uuid",
- GNUNET_JSON_from_data_auto (&cd->uuid));
- set_state (sctx->state,
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
- sctx->cb (sctx->cb_cls,
- TALER_EC_NONE,
- sctx->state);
- sctx_free (sctx);
- return;
}
GNUNET_break (0);
ANASTASIS_redux_fail_ (sctx->cb,
@@ -1197,21 +1265,19 @@ solve_challenge_cb (void *cls,
sctx_free (sctx);
return;
}
- ret = ANASTASIS_challenge_start (ci,
- psp,
- timeout,
- &hashed_answer,
- &answer_feedback_cb,
- sctx);
+ ret = ANASTASIS_challenge_answer3 (ci,
+ psp,
+ timeout,
+ &hashed_answer,
+ &answer_feedback_cb,
+ sctx);
}
else
{
/* no answer provided */
ret = ANASTASIS_challenge_start (ci,
psp,
- timeout,
- NULL, /* no answer */
- &answer_feedback_cb,
+ &start_feedback_cb,
sctx);
}
}
@@ -1331,9 +1397,7 @@ pay_challenge_cb (void *cls,
{
ret = ANASTASIS_challenge_start (ci,
&sctx->ps,
- sctx->timeout,
- NULL, /* no answer yet */
- &answer_feedback_cb,
+ &start_feedback_cb,
sctx);
}
if (GNUNET_OK != ret)
@@ -1780,9 +1844,7 @@ select_challenge_cb (void *cls,
{
ret = ANASTASIS_challenge_start (ci,
psp,
- timeout,
- NULL, /* no answer */
- &answer_feedback_cb,
+ &start_feedback_cb,
sctx);
}
}
diff --git a/src/restclient/Makefile.am b/src/restclient/Makefile.am
index b2e9d0b..9bee2dd 100644
--- a/src/restclient/Makefile.am
+++ b/src/restclient/Makefile.am
@@ -22,7 +22,6 @@ libanastasisrest_la_SOURCES = \
anastasis_api_truth_challenge.c \
anastasis_api_truth_solve.c \
anastasis_api_truth_store.c \
- anastasis_api_keyshare_lookup.c \
anastasis_api_curl_defaults.c anastasis_api_curl_defaults.h
libanastasisrest_la_LIBADD = \
-lgnunetcurl \
diff --git a/src/restclient/anastasis_api_curl_defaults.c b/src/restclient/anastasis_api_curl_defaults.c
index e052517..33665e0 100644
--- a/src/restclient/anastasis_api_curl_defaults.c
+++ b/src/restclient/anastasis_api_curl_defaults.c
@@ -42,5 +42,17 @@ ANASTASIS_curl_easy_get_ (const char *url)
curl_easy_setopt (eh,
CURLOPT_TCP_FASTOPEN,
1L));
+ /* limit MAXREDIRS to 5 as a simple security measure against
+ a potential infinite loop caused by a malicious target */
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_MAXREDIRS,
+ 5L));
+ /* Enable compression (using whatever curl likes), see
+ https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html */
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_ACCEPT_ENCODING,
+ ""));
return eh;
}
diff --git a/src/restclient/anastasis_api_truth_challenge.c b/src/restclient/anastasis_api_truth_challenge.c
index 92916d6..911eba6 100644
--- a/src/restclient/anastasis_api_truth_challenge.c
+++ b/src/restclient/anastasis_api_truth_challenge.c
@@ -123,6 +123,7 @@ handle_truth_challenge_finished (void *cls,
{
const char *ct;
const char *tan_hint = NULL;
+ const char *filename = NULL;
json_t *wire_details = NULL;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string (
@@ -132,6 +133,9 @@ handle_truth_challenge_finished (void *cls,
GNUNET_JSON_spec_string ("tan_address_hint",
&tan_hint)),
GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("filename",
+ &filename)),
+ GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("wire_details",
&wire_details)),
GNUNET_JSON_spec_end ()
@@ -152,19 +156,34 @@ handle_truth_challenge_finished (void *cls,
{
tcd.details.success.cs = ANASTASIS_CS_TAN_SENT;
tcd.details.success.details.tan_address_hint = tan_hint;
+ break;
+ }
+ if ( (0 == strcmp (ct,
+ "FILE_WRITTEN")) &&
+ (NULL != filename) )
+ {
+ tcd.details.success.cs = ANASTASIS_CS_FILE_WRITTEN;
+ tcd.details.success.details.challenge_filename = filename;
+ break;
}
- else if ( (0 == strcmp (ct,
- "WIRE_FUNDS")) &&
- (NULL != wire_details) )
+ if ( (0 == strcmp (ct,
+ "IBAN_WIRE")) &&
+ (NULL != wire_details) )
{
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_string (
- "target_account",
- &tcd.details.success.details.wire_funds.target_payto),
+ "credit_iban",
+ &tcd.details.success.details.wire_funds.target_iban),
+ GNUNET_JSON_spec_uint64 (
+ "answer_code",
+ &tcd.details.success.details.wire_funds.answer_code),
+ GNUNET_JSON_spec_string (
+ "business_name",
+ &tcd.details.success.details.wire_funds.target_business_name),
GNUNET_JSON_spec_string (
- "sender_hint",
- &tcd.details.success.details.wire_funds.sender_hint),
- TALER_JSON_spec_amount_any ("amount",
+ "wire_transfer_subject",
+ &tcd.details.success.details.wire_funds.wire_transfer_subject),
+ TALER_JSON_spec_amount_any ("challenge_amount",
&tcd.details.success.details.wire_funds.
amount),
GNUNET_JSON_spec_end ()
@@ -188,6 +207,12 @@ handle_truth_challenge_finished (void *cls,
ANASTASIS_truth_challenge_cancel (tco);
return;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected challenge type `%s'\n",
+ ct);
+ tcd.http_status = 0;
+ tcd.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ break;
}
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the anastasis server is buggy
@@ -409,10 +434,11 @@ ANASTASIS_truth_challenge (
curl_easy_setopt (eh,
CURLOPT_HEADERDATA,
tco));
- tco->job = GNUNET_CURL_job_add (ctx,
- eh,
- &handle_truth_challenge_finished,
- tco);
+ tco->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ tco->ctx.headers,
+ &handle_truth_challenge_finished,
+ tco);
return tco;
}
diff --git a/src/restclient/anastasis_api_truth_solve.c b/src/restclient/anastasis_api_truth_solve.c
index 376fc74..971e917 100644
--- a/src/restclient/anastasis_api_truth_solve.c
+++ b/src/restclient/anastasis_api_truth_solve.c
@@ -427,7 +427,7 @@ ANASTASIS_truth_solve (
tso));
tso->job = GNUNET_CURL_job_add_raw (ctx,
eh,
- NULL,
+ tso->ctx.headers,
&handle_truth_solve_finished,
tso);
return tso;
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index 9a98530..fec971a 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -14,9 +14,10 @@ libanastasistesting_la_LDFLAGS = \
-no-undefined
libanastasistesting_la_SOURCES = \
testing_api_cmd_policy_store.c \
+ testing_api_cmd_truth_challenge.c \
+ testing_api_cmd_truth_solve.c \
testing_api_cmd_truth_store.c \
testing_api_cmd_policy_lookup.c \
- testing_api_cmd_keyshare_lookup.c \
testing_api_cmd_config.c \
testing_api_helpers.c \
testing_api_traits.c \
diff --git a/src/testing/test_anastasis.c b/src/testing/test_anastasis.c
index 6ce6771..7b127ac 100644
--- a/src/testing/test_anastasis.c
+++ b/src/testing/test_anastasis.c
@@ -280,7 +280,7 @@ run (void *cls,
0, /* challenge index */
"SomeTruth1",
0, /* mode */
- ANASTASIS_CHALLENGE_STATUS_SOLVED),
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED),
#if 0
ANASTASIS_TESTING_cmd_challenge_answer ("challenge-answer-2",
NULL, /* payment ref */
@@ -288,13 +288,13 @@ run (void *cls,
1, /* challenge index */
"SomeTruth2",
0, /* mode */
- ANASTASIS_CHALLENGE_STATUS_SOLVED),
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED),
#endif
ANASTASIS_TESTING_cmd_challenge_start ("challenge-start-3-pay",
NULL, /* payment ref */
"recover-secret-1",
2, /* challenge index */
- ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED),
+ ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED),
TALER_TESTING_cmd_merchant_claim_order ("fetch-challenge-pay-proposal",
merchant_url,
MHD_HTTP_OK,
@@ -312,14 +312,14 @@ run (void *cls,
"challenge-start-3-pay", /* payment ref */
"recover-secret-1",
2, /* challenge index */
- ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS),
+ ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED),
ANASTASIS_TESTING_cmd_challenge_answer ("challenge-answer-3",
"challenge-start-3-pay", /* payment ref */
"recover-secret-1",
2, /* challenge index */
"challenge-start-3-paid", /* answer */
1, /* mode */
- ANASTASIS_CHALLENGE_STATUS_SOLVED),
+ ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED),
ANASTASIS_TESTING_cmd_recover_secret_finish ("recover-finish-1",
"recover-secret-1",
GNUNET_TIME_UNIT_SECONDS),
diff --git a/src/testing/test_anastasis_api.c b/src/testing/test_anastasis_api.c
index b8d9608..caa59c8 100644
--- a/src/testing/test_anastasis_api.c
+++ b/src/testing/test_anastasis_api.c
@@ -209,14 +209,14 @@ run (void *cls,
"The-Answer",
ANASTASIS_TESTING_TSO_NONE,
MHD_HTTP_NO_CONTENT),
- ANASTASIS_TESTING_cmd_keyshare_lookup (
+ ANASTASIS_TESTING_cmd_truth_solve (
"keyshare-lookup-1",
anastasis_url,
"The-Answer",
NULL, /* payment ref */
"truth-store-1",
0,
- ANASTASIS_KSD_SUCCESS),
+ MHD_HTTP_OK),
ANASTASIS_TESTING_cmd_truth_store (
"truth-store-2",
anastasis_url,
@@ -227,22 +227,20 @@ run (void *cls,
file_secret,
ANASTASIS_TESTING_TSO_NONE,
MHD_HTTP_NO_CONTENT),
- ANASTASIS_TESTING_cmd_keyshare_lookup (
+ ANASTASIS_TESTING_cmd_truth_solve (
"challenge-fail-1",
anastasis_url,
"Wrong-Answer",
- NULL,
- "truth-store-1",
- 0,
- ANASTASIS_KSD_INVALID_ANSWER),
- ANASTASIS_TESTING_cmd_keyshare_lookup (
+ NULL, /* payment ref */
+ "truth-store-1", /* upload ref */
+ 0, /* security question mode */
+ MHD_HTTP_FORBIDDEN),
+ ANASTASIS_TESTING_cmd_truth_challenge (
"file-challenge-run-1",
anastasis_url,
- NULL, /* no answer */
NULL, /* payment ref */
"truth-store-2", /* upload ref */
- 0,
- ANASTASIS_KSD_PAYMENT_REQUIRED),
+ MHD_HTTP_PAYMENT_REQUIRED),
/* what would we have to pay? */
TALER_TESTING_cmd_merchant_claim_order ("fetch-proposal-2",
merchant_url,
@@ -259,22 +257,20 @@ run (void *cls,
"EUR:1",
NULL),
- ANASTASIS_TESTING_cmd_keyshare_lookup (
+ ANASTASIS_TESTING_cmd_truth_challenge (
"file-challenge-run-2",
anastasis_url,
- NULL, /* no answer */
"file-challenge-run-1", /* payment ref */
"truth-store-2",
- 0,
- ANASTASIS_KSD_INVALID_ANSWER),
- ANASTASIS_TESTING_cmd_keyshare_lookup (
+ MHD_HTTP_OK),
+ ANASTASIS_TESTING_cmd_truth_solve (
"file-challenge-run-3",
anastasis_url,
"file-challenge-run-2", /* answer */
"file-challenge-run-1", /* payment ref */
"truth-store-2",
1,
- ANASTASIS_KSD_SUCCESS),
+ MHD_HTTP_OK),
TALER_TESTING_cmd_end ()
};
diff --git a/src/testing/testing_api_cmd_truth_challenge.c b/src/testing/testing_api_cmd_truth_challenge.c
new file mode 100644
index 0000000..c584d5f
--- /dev/null
+++ b/src/testing/testing_api_cmd_truth_challenge.c
@@ -0,0 +1,372 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2020, 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing/testing_api_cmd_truth_challenge.c
+ * @brief Testing of Implementation of the /truth GET
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ * @author Dominik Meister
+ */
+
+#include "platform.h"
+#include "anastasis_testing_lib.h"
+#include <taler/taler_util.h>
+#include <taler/taler_testing_lib.h>
+#include <taler/taler_merchant_service.h>
+
+
+/**
+ * State for a "keyshare lookup" CMD.
+ */
+struct TruthChallengeState
+{
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * URL of the anastasis backend.
+ */
+ const char *anastasis_url;
+
+ /**
+ * Expected HTTP status code.
+ */
+ unsigned int expected_http_status;
+
+ /**
+ * The /truth GET operation handle.
+ */
+ struct ANASTASIS_TruthChallengeOperation *tco;
+
+ /**
+ * Reference to upload command we expect to lookup.
+ */
+ const char *upload_reference;
+
+ /**
+ * Reference to upload command we expect to lookup.
+ */
+ const char *payment_reference;
+
+ /**
+ * Payment secret requested by the service, if any.
+ */
+ struct ANASTASIS_PaymentSecretP payment_secret_response;
+
+ /**
+ * Taler-URI with payment request, if any.
+ */
+ char *pay_uri;
+
+ /**
+ * Order ID for payment request, if any.
+ */
+ char *order_id;
+
+ /**
+ * "code" returned by service, if any.
+ */
+ char *code;
+
+ /**
+ * "instructions" for how to solve the challenge as returned by service, if any.
+ */
+ char *instructions;
+
+};
+
+
+static void
+truth_challenge_cb (void *cls,
+ const struct ANASTASIS_TruthChallengeDetails *tcd)
+{
+ struct TruthChallengeState *ksls = cls;
+
+ ksls->tco = NULL;
+ if (tcd->http_status != ksls->expected_http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s in %s:%u\n",
+ tcd->http_status,
+ ksls->is->commands[ksls->is->ip].label,
+ __FILE__,
+ __LINE__);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ switch (tcd->http_status)
+ {
+ case MHD_HTTP_OK:
+ switch (tcd->details.success.cs)
+ {
+ case ANASTASIS_CS_FILE_WRITTEN:
+ {
+ FILE *file;
+ char code[22];
+
+ file = fopen (tcd->details.success.details.challenge_filename,
+ "r");
+ if (NULL == file)
+ {
+ GNUNET_log_strerror_file (
+ GNUNET_ERROR_TYPE_ERROR,
+ "open",
+ tcd->details.success.details.challenge_filename);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ if (0 == fscanf (file,
+ "%21s",
+ code))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "fscanf",
+ tcd->details.success.details.
+ challenge_filename);
+ GNUNET_break (0 == fclose (file));
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ GNUNET_break (0 == fclose (file));
+ ksls->code = GNUNET_strdup (code);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Read code `%s'\n",
+ code);
+ }
+ break;
+ case ANASTASIS_CS_TAN_SENT:
+ ksls->instructions = GNUNET_strdup (
+ tcd->details.success.details.tan_address_hint);
+ break;
+ case ANASTASIS_CS_WIRE_FUNDS:
+ /* FIXME: not implemented */
+ GNUNET_break (0);
+ return;
+ }
+ break;
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ ksls->pay_uri = GNUNET_strdup (
+ tcd->details.payment_required.payment_request);
+ ksls->payment_secret_response = tcd->details.payment_required.ps;
+ {
+ struct TALER_MERCHANT_PayUriData pd;
+
+ if (GNUNET_OK !=
+ TALER_MERCHANT_parse_pay_uri (ksls->pay_uri,
+ &pd))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ ksls->order_id = GNUNET_strdup (pd.order_id);
+ TALER_MERCHANT_parse_pay_uri_free (&pd);
+ }
+
+ break;
+ default:
+ break;
+ }
+ TALER_TESTING_interpreter_next (ksls->is);
+}
+
+
+static void
+truth_challenge_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct TruthChallengeState *ksls = cls;
+ const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key;
+ const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid;
+ const struct ANASTASIS_PaymentSecretP *payment_secret;
+
+ ksls->is = is;
+ if (NULL == ksls->upload_reference)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ {
+ const struct TALER_TESTING_Command *upload_cmd;
+
+ upload_cmd = TALER_TESTING_interpreter_lookup_command (
+ is,
+ ksls->upload_reference);
+ if (NULL == upload_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ if (GNUNET_OK !=
+ ANASTASIS_TESTING_get_trait_truth_uuid (upload_cmd,
+ &truth_uuid))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ if (NULL == truth_uuid)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ if (GNUNET_OK !=
+ ANASTASIS_TESTING_get_trait_truth_key (upload_cmd,
+ &truth_key))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ if (NULL == truth_key)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ }
+
+ if (NULL != ksls->payment_reference)
+ {
+ const struct TALER_TESTING_Command *payment_cmd;
+
+ payment_cmd = TALER_TESTING_interpreter_lookup_command (
+ is,
+ ksls->payment_reference);
+ if (GNUNET_OK !=
+ ANASTASIS_TESTING_get_trait_payment_secret (payment_cmd,
+ &payment_secret))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+ }
+ else
+ {
+ payment_secret = NULL;
+ }
+
+ ksls->tco = ANASTASIS_truth_challenge (is->ctx,
+ ksls->anastasis_url,
+ truth_uuid,
+ truth_key,
+ payment_secret,
+ &truth_challenge_cb,
+ ksls);
+ if (NULL == ksls->tco)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
+}
+
+
+static void
+truth_challenge_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct TruthChallengeState *ksls = cls;
+
+ if (NULL != ksls->tco)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command '%s' did not complete (keyshare lookup)\n",
+ cmd->label);
+ ANASTASIS_truth_challenge_cancel (ksls->tco);
+ ksls->tco = NULL;
+ }
+ GNUNET_free (ksls->pay_uri);
+ GNUNET_free (ksls->order_id);
+ GNUNET_free (ksls->code);
+ GNUNET_free (ksls->instructions);
+ GNUNET_free (ksls);
+}
+
+
+/**
+ * Offer internal data to other commands.
+ *
+ * @param cls closure
+ * @param[out] ret result (could be anything)
+ * @param[out] trait name of the trait
+ * @param index index number of the object to extract.
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+truth_challenge_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct TruthChallengeState *ksls = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ ANASTASIS_TESTING_make_trait_payment_secret (
+ &ksls->payment_secret_response),
+ TALER_TESTING_make_trait_payto_uri (
+ (const char **) ksls->pay_uri),
+ TALER_TESTING_make_trait_order_id (
+ (const char **) &ksls->order_id),
+ ANASTASIS_TESTING_make_trait_code (
+ (const char **) &ksls->code),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+struct TALER_TESTING_Command
+ANASTASIS_TESTING_cmd_truth_challenge (
+ const char *label,
+ const char *anastasis_url,
+ const char *payment_ref,
+ const char *upload_ref,
+ unsigned int http_status)
+{
+ struct TruthChallengeState *ksls;
+
+ GNUNET_assert (NULL != upload_ref);
+ ksls = GNUNET_new (struct TruthChallengeState);
+ ksls->expected_http_status = http_status;
+ ksls->anastasis_url = anastasis_url;
+ ksls->upload_reference = upload_ref;
+ ksls->payment_reference = payment_ref;
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = ksls,
+ .label = label,
+ .run = &truth_challenge_run,
+ .cleanup = &truth_challenge_cleanup,
+ .traits = &truth_challenge_traits
+ };
+
+ return cmd;
+ }
+}
+
+
+/* end of testing_api_cmd_truth_challenge.c */
diff --git a/src/testing/testing_api_cmd_keyshare_lookup.c b/src/testing/testing_api_cmd_truth_solve.c
index 8b06a67..5c12b3f 100644
--- a/src/testing/testing_api_cmd_keyshare_lookup.c
+++ b/src/testing/testing_api_cmd_truth_solve.c
@@ -1,6 +1,6 @@
/*
This file is part of Anastasis
- Copyright (C) 2020 Anastasis SARL
+ Copyright (C) 2020, 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
@@ -14,7 +14,7 @@
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file testing/testing_api_cmd_keyshare_lookup.c
+ * @file testing/testing_api_cmd_truth_solve.c
* @brief Testing of Implementation of the /truth GET
* @author Christian Grothoff
* @author Dennis Neufeld
@@ -31,7 +31,7 @@
/**
* State for a "keyshare lookup" CMD.
*/
-struct KeyShareLookupState
+struct TruthSolveState
{
/**
* The interpreter state.
@@ -46,12 +46,18 @@ struct KeyShareLookupState
/**
* Expected status code.
*/
- enum ANASTASIS_KeyShareDownloadStatus expected_ksdd;
+ unsigned int expected_http_status;
+
+ /**
+ * Resulting encrypted key share.
+ * Note: currently not used.
+ */
+ struct ANASTASIS_CRYPTO_EncryptedKeyShareP eks;
/**
* The /truth GET operation handle.
*/
- struct ANASTASIS_KeyShareLookupOperation *kslo;
+ struct ANASTASIS_TruthSolveOperation *tso;
/**
* answer to a challenge
@@ -113,100 +119,35 @@ struct KeyShareLookupState
static void
-keyshare_lookup_cb (void *cls,
- const struct ANASTASIS_KeyShareDownloadDetails *dd)
+truth_solve_cb (void *cls,
+ const struct ANASTASIS_TruthSolveReply *tsr)
{
- struct KeyShareLookupState *ksls = cls;
+ struct TruthSolveState *ksls = cls;
- ksls->kslo = NULL;
- if (dd->status != ksls->expected_ksdd)
+ ksls->tso = NULL;
+ if (tsr->http_status != ksls->expected_http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s in %s:%u\n",
- dd->status,
+ tsr->http_status,
ksls->is->commands[ksls->is->ip].label,
__FILE__,
__LINE__);
TALER_TESTING_interpreter_fail (ksls->is);
return;
}
- switch (dd->status)
+ switch (tsr->http_status)
{
- case ANASTASIS_KSD_SUCCESS:
- break;
- case ANASTASIS_KSD_PAYMENT_REQUIRED:
- ksls->pay_uri = GNUNET_strdup (dd->details.payment_required.taler_pay_uri);
- ksls->payment_secret_response = dd->details.payment_required.payment_secret;
- {
- struct TALER_MERCHANT_PayUriData pd;
-
- if (GNUNET_OK !=
- TALER_MERCHANT_parse_pay_uri (ksls->pay_uri,
- &pd))
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (ksls->is);
- return;
- }
- ksls->order_id = GNUNET_strdup (pd.order_id);
- TALER_MERCHANT_parse_pay_uri_free (&pd);
- }
-
- break;
- case ANASTASIS_KSD_INVALID_ANSWER:
- if (ksls->filename)
- {
- FILE *file;
- char code[22];
-
- file = fopen (ksls->filename,
- "r");
- if (NULL == file)
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "open",
- ksls->filename);
- TALER_TESTING_interpreter_fail (ksls->is);
- return;
- }
- if (0 == fscanf (file,
- "%21s",
- code))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "fscanf",
- ksls->filename);
- GNUNET_break (0 == fclose (file));
- TALER_TESTING_interpreter_fail (ksls->is);
- return;
- }
- GNUNET_break (0 == fclose (file));
- ksls->code = GNUNET_strdup (code);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Read code `%s'\n",
- code);
- }
- else
- {
- ksls->instructions = GNUNET_strndup (
- dd->details.open_challenge.body,
- dd->details.open_challenge.body_size);
- }
- break;
- case ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION:
- ksls->redirect_uri = GNUNET_strdup (dd->details.redirect_url);
+ case MHD_HTTP_OK:
+ ksls->eks = tsr->details.success.eks;
break;
- case ANASTASIS_KSD_SERVER_ERROR:
+ case MHD_HTTP_PAYMENT_REQUIRED:
+ ksls->pay_uri = GNUNET_strdup (
+ tsr->details.payment_required.payment_request);
+ ksls->payment_secret_response = tsr->details.payment_required.ps;
+ ksls->order_id = GNUNET_strdup (tsr->details.payment_required.pd->order_id);
break;
- case ANASTASIS_KSD_CLIENT_FAILURE:
- break;
- case ANASTASIS_KSD_TRUTH_UNKNOWN:
- break;
- case ANASTASIS_KSD_RATE_LIMIT_EXCEEDED:
- break;
- case ANASTASIS_KSD_AUTHENTICATION_TIMEOUT:
- break;
- case ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS:
+ default:
break;
}
TALER_TESTING_interpreter_next (ksls->is);
@@ -214,11 +155,11 @@ keyshare_lookup_cb (void *cls,
static void
-keyshare_lookup_run (void *cls,
- const struct TALER_TESTING_Command *cmd,
- struct TALER_TESTING_Interpreter *is)
+truth_solve_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
{
- struct KeyShareLookupState *ksls = cls;
+ struct TruthSolveState *ksls = cls;
const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key;
const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid;
const struct ANASTASIS_PaymentSecretP *payment_secret;
@@ -319,6 +260,12 @@ keyshare_lookup_run (void *cls,
/* answer is the answer */
answerp = &ksls->answer;
}
+ if (NULL == answerp)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ksls->is);
+ return;
+ }
if (NULL != ksls->payment_reference)
{
@@ -344,23 +291,20 @@ keyshare_lookup_run (void *cls,
{
struct GNUNET_HashCode h_answer;
- if (NULL != *answerp)
- GNUNET_CRYPTO_hash (*answerp,
- strlen (*answerp),
- &h_answer);
- ksls->kslo = ANASTASIS_keyshare_lookup (is->ctx,
- ksls->anastasis_url,
- truth_uuid,
- truth_key,
- payment_secret,
- GNUNET_TIME_UNIT_ZERO,
- (NULL != *answerp)
- ? &h_answer
- : NULL,
- &keyshare_lookup_cb,
- ksls);
+ GNUNET_CRYPTO_hash (*answerp,
+ strlen (*answerp),
+ &h_answer);
+ ksls->tso = ANASTASIS_truth_solve (is->ctx,
+ ksls->anastasis_url,
+ truth_uuid,
+ truth_key,
+ payment_secret,
+ GNUNET_TIME_UNIT_ZERO,
+ &h_answer,
+ &truth_solve_cb,
+ ksls);
}
- if (NULL == ksls->kslo)
+ if (NULL == ksls->tso)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ksls->is);
@@ -370,18 +314,18 @@ keyshare_lookup_run (void *cls,
static void
-keyshare_lookup_cleanup (void *cls,
- const struct TALER_TESTING_Command *cmd)
+truth_solve_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
{
- struct KeyShareLookupState *ksls = cls;
+ struct TruthSolveState *ksls = cls;
- if (NULL != ksls->kslo)
+ if (NULL != ksls->tso)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command '%s' did not complete (keyshare lookup)\n",
cmd->label);
- ANASTASIS_keyshare_lookup_cancel (ksls->kslo);
- ksls->kslo = NULL;
+ ANASTASIS_truth_solve_cancel (ksls->tso);
+ ksls->tso = NULL;
}
GNUNET_free (ksls->pay_uri);
GNUNET_free (ksls->order_id);
@@ -403,12 +347,12 @@ keyshare_lookup_cleanup (void *cls,
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-keyshare_lookup_traits (void *cls,
- const void **ret,
- const char *trait,
- unsigned int index)
+truth_solve_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
{
- struct KeyShareLookupState *ksls = cls;
+ struct TruthSolveState *ksls = cls;
struct TALER_TESTING_Trait traits[] = {
ANASTASIS_TESTING_make_trait_payment_secret (
&ksls->payment_secret_response),
@@ -429,20 +373,20 @@ keyshare_lookup_traits (void *cls,
struct TALER_TESTING_Command
-ANASTASIS_TESTING_cmd_keyshare_lookup (
+ANASTASIS_TESTING_cmd_truth_solve (
const char *label,
const char *anastasis_url,
const char *answer,
const char *payment_ref,
const char *upload_ref,
int lookup_mode,
- enum ANASTASIS_KeyShareDownloadStatus ksdd)
+ unsigned int http_status)
{
- struct KeyShareLookupState *ksls;
+ struct TruthSolveState *ksls;
GNUNET_assert (NULL != upload_ref);
- ksls = GNUNET_new (struct KeyShareLookupState);
- ksls->expected_ksdd = ksdd;
+ ksls = GNUNET_new (struct TruthSolveState);
+ ksls->expected_http_status = http_status;
ksls->anastasis_url = anastasis_url;
ksls->upload_reference = upload_ref;
ksls->payment_reference = payment_ref;
@@ -452,9 +396,9 @@ ANASTASIS_TESTING_cmd_keyshare_lookup (
struct TALER_TESTING_Command cmd = {
.cls = ksls,
.label = label,
- .run = &keyshare_lookup_run,
- .cleanup = &keyshare_lookup_cleanup,
- .traits = &keyshare_lookup_traits
+ .run = &truth_solve_run,
+ .cleanup = &truth_solve_cleanup,
+ .traits = &truth_solve_traits
};
return cmd;
@@ -462,4 +406,4 @@ ANASTASIS_TESTING_cmd_keyshare_lookup (
}
-/* end of testing_api_cmd_keyshare_lookup.c */
+/* end of testing_api_cmd_truth_solve.c */
diff --git a/src/testing/testing_cmd_challenge_answer.c b/src/testing/testing_cmd_challenge_answer.c
index 78f7404..88c4c2f 100644
--- a/src/testing/testing_cmd_challenge_answer.c
+++ b/src/testing/testing_cmd_challenge_answer.c
@@ -1,6 +1,6 @@
/*
This file is part of Anastasis
- Copyright (C) 2020, 2021 Anastasis SARL
+ 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
@@ -20,7 +20,6 @@
* @author Dennis Neufeld
* @author Dominik Meister
*/
-
#include "platform.h"
#include "anastasis_testing_lib.h"
#include <taler/taler_util.h>
@@ -28,6 +27,8 @@
#include <taler/taler_merchant_service.h>
+// FIXME: break up into two files, one for start, one for answer!
+
/**
* State for a "challenge answer" CMD.
*/
@@ -74,9 +75,14 @@ struct ChallengeState
struct ANASTASIS_PaymentSecretP payment_order_req;
/**
- * Expected status code.
+ * Expected answer status code.
+ */
+ enum ANASTASIS_ChallengeAnswerStatus expected_acs;
+
+ /**
+ * Expected start status code.
*/
- enum ANASTASIS_ChallengeStatus expected_cs;
+ enum ANASTASIS_ChallengeStartStatus expected_scs;
/**
* Index of the challenge we are solving
@@ -98,97 +104,28 @@ struct ChallengeState
static void
challenge_answer_cb (void *af_cls,
- const struct ANASTASIS_ChallengeStartResponse *csr)
+ const struct ANASTASIS_ChallengeAnswerResponse *csr)
{
struct ChallengeState *cs = af_cls;
cs->c = NULL;
- if (csr->cs != cs->expected_cs)
+ if (csr->cs != cs->expected_acs)
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Expected status %u, got %u\n",
- cs->expected_cs,
+ cs->expected_acs,
csr->cs);
TALER_TESTING_interpreter_fail (cs->is);
return;
}
switch (csr->cs)
{
- case ANASTASIS_CHALLENGE_STATUS_SOLVED:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED:
break;
- case ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS:
- {
- FILE *file;
- char *fn;
-
- if (0 == strcasecmp (csr->details.open_challenge.content_type,
- "application/json"))
- {
- const char *filename;
- json_t *in;
-
- in = json_loadb (csr->details.open_challenge.body,
- csr->details.open_challenge.body_size,
- JSON_REJECT_DUPLICATES,
- NULL);
- if (NULL == in)
- {
- GNUNET_break (0);
- TALER_TESTING_interpreter_fail (cs->is);
- return;
- }
- filename = json_string_value (json_object_get (in,
- "filename"));
- if (NULL == filename)
- {
- GNUNET_break (0);
- json_decref (in);
- TALER_TESTING_interpreter_fail (cs->is);
- return;
- }
- fn = GNUNET_strdup (filename);
- json_decref (in);
- }
- else
- {
- fn = GNUNET_strndup (csr->details.open_challenge.body,
- csr->details.open_challenge.body_size);
- }
- file = fopen (fn,
- "r");
- if (NULL == file)
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "open",
- fn);
- GNUNET_free (fn);
- TALER_TESTING_interpreter_fail (cs->is);
- return;
- }
- cs->code = GNUNET_malloc (22);
- if (0 == fscanf (file,
- "%21s",
- cs->code))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "fscanf",
- fn);
- TALER_TESTING_interpreter_fail (cs->is);
- fclose (file);
- GNUNET_free (fn);
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Read challenge answer `%s' from file `%s'\n",
- cs->code,
- fn);
- TALER_TESTING_interpreter_next (cs->is);
- GNUNET_break (0 == fclose (file));
- GNUNET_free (fn);
- return;
- }
- case ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED:
+ 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/")))
@@ -228,19 +165,15 @@ challenge_answer_cb (void *af_cls,
}
TALER_TESTING_interpreter_next (cs->is);
return;
- case ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN:
- break;
- case ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN:
break;
- case ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE:
GNUNET_break (0);
TALER_TESTING_interpreter_fail (cs->is);
return;
- case ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED:
break;
- case ANASTASIS_CHALLENGE_STATUS_AUTH_TIMEOUT:
- break;
- case ANASTASIS_CHALLENGE_STATUS_EXTERNAL_INSTRUCTIONS:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_AUTH_TIMEOUT:
break;
}
TALER_TESTING_interpreter_next (cs->is);
@@ -382,6 +315,115 @@ challenge_answer_run (void *cls,
}
+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.
*
@@ -446,9 +488,7 @@ challenge_start_run (void *cls,
if (GNUNET_OK !=
ANASTASIS_challenge_start ((struct ANASTASIS_Challenge *) *c,
ps,
- GNUNET_TIME_UNIT_ZERO,
- NULL,
- &challenge_answer_cb,
+ &challenge_start_cb,
cs))
{
GNUNET_break (0);
@@ -527,12 +567,12 @@ ANASTASIS_TESTING_cmd_challenge_start (
const char *payment_ref,
const char *challenge_ref,
unsigned int challenge_index,
- enum ANASTASIS_ChallengeStatus expected_cs)
+ enum ANASTASIS_ChallengeStartStatus expected_cs)
{
struct ChallengeState *cs;
cs = GNUNET_new (struct ChallengeState);
- cs->expected_cs = expected_cs;
+ cs->expected_scs = expected_cs;
cs->challenge_ref = challenge_ref;
cs->payment_ref = payment_ref;
cs->challenge_index = challenge_index;
@@ -558,12 +598,12 @@ ANASTASIS_TESTING_cmd_challenge_answer (
unsigned int challenge_index,
const char *answer,
unsigned int mode,
- enum ANASTASIS_ChallengeStatus expected_cs)
+ enum ANASTASIS_ChallengeAnswerStatus expected_cs)
{
struct ChallengeState *cs;
cs = GNUNET_new (struct ChallengeState);
- cs->expected_cs = expected_cs;
+ cs->expected_acs = expected_cs;
cs->challenge_ref = challenge_ref;
cs->payment_ref = payment_ref;
cs->answer = answer;