anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis-httpd_truth-solve.c (47590B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2019-2022 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Anastasis; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis-httpd_truth-solve.c
     18  * @brief functions to handle incoming requests on /truth/$TID/solve
     19  * @author Dennis Neufeld
     20  * @author Dominik Meister
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"
     24 struct SolveContext;
     25 #define TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE struct SolveContext
     26 #define TALER_MERCHANT_GET_PRIVATE_ORDER_RESULT_CLOSURE struct SolveContext
     27 #include "anastasis-httpd.h"
     28 #include "anastasis_service.h"
     29 #include "anastasis-httpd_truth.h"
     30 #include <gnunet/gnunet_util_lib.h>
     31 #include <gnunet/gnunet_rest_lib.h>
     32 #include "anastasis_authorization_lib.h"
     33 #include <taler/taler_merchant_service.h>
     34 #include <taler/taler_json_lib.h>
     35 #include <taler/taler_mhd_lib.h>
     36 #include <taler/merchant/post-private-orders.h>
     37 #include <taler/merchant/get-private-orders-ORDER_ID.h>
     38 
     39 /**
     40  * What is the maximum frequency at which we allow
     41  * clients to attempt to answer security questions?
     42  */
     43 #define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \
     44           GNUNET_TIME_UNIT_SECONDS, 30)
     45 
     46 /**
     47  * How long should the wallet check for auto-refunds before giving up?
     48  */
     49 #define AUTO_REFUND_TIMEOUT GNUNET_TIME_relative_multiply ( \
     50           GNUNET_TIME_UNIT_MINUTES, 2)
     51 
     52 
     53 /**
     54  * How many retries do we allow per code?
     55  */
     56 #define INITIAL_RETRY_COUNTER 3
     57 
     58 
     59 struct SolveContext
     60 {
     61 
     62   /**
     63    * Payment Identifier
     64    */
     65   struct ANASTASIS_PaymentSecretP payment_identifier;
     66 
     67   /**
     68    * Public key of the challenge which is solved.
     69    */
     70   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     71 
     72   /**
     73    * Key to decrypt the truth.
     74    */
     75   struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
     76 
     77   /**
     78    * Cost for paying the challenge.
     79    */
     80   struct TALER_Amount challenge_cost;
     81 
     82   /**
     83    * Our handler context.
     84    */
     85   struct TM_HandlerContext *hc;
     86 
     87   /**
     88    * Opaque parsing context.
     89    */
     90   void *opaque_post_parsing_context;
     91 
     92   /**
     93    * Uploaded JSON data, NULL if upload is not yet complete.
     94    */
     95   json_t *root;
     96 
     97   /**
     98    * Kept in DLL for shutdown handling while suspended.
     99    */
    100   struct SolveContext *next;
    101 
    102   /**
    103    * Kept in DLL for shutdown handling while suspended.
    104    */
    105   struct SolveContext *prev;
    106 
    107   /**
    108    * Connection handle for closing or resuming
    109    */
    110   struct MHD_Connection *connection;
    111 
    112   /**
    113    * Reference to the authorization plugin which was loaded
    114    */
    115   struct ANASTASIS_AuthorizationPlugin *authorization;
    116 
    117   /**
    118    * Status of the authorization
    119    */
    120   struct ANASTASIS_AUTHORIZATION_State *as;
    121 
    122   /**
    123    * Used while we are awaiting proposal creation.
    124    */
    125   struct TALER_MERCHANT_PostPrivateOrdersHandle *po;
    126 
    127   /**
    128    * Used while we are waiting payment.
    129    */
    130   struct TALER_MERCHANT_GetPrivateOrderHandle *cpo;
    131 
    132   /**
    133    * HTTP response code to use on resume, if non-NULL.
    134    */
    135   struct MHD_Response *resp;
    136 
    137   /**
    138    * Our entry in the #to_heap, or NULL.
    139    */
    140   struct GNUNET_CONTAINER_HeapNode *hn;
    141 
    142   /**
    143    * Challenge response we got from the request.
    144    */
    145   struct GNUNET_HashCode challenge_response;
    146 
    147   /**
    148    * How long do we wait at most for payment or
    149    * authorization?
    150    */
    151   struct GNUNET_TIME_Absolute timeout;
    152 
    153   /**
    154    * Random authorization code we are using.
    155    */
    156   uint64_t code;
    157 
    158   /**
    159    * HTTP response code to use on resume, if resp is set.
    160    */
    161   unsigned int response_code;
    162 
    163   /**
    164    * true if client did not provide a payment secret / order ID.
    165    */
    166   bool no_payment_identifier_provided;
    167 
    168   /**
    169    * True if this entry is in the #gc_head DLL.
    170    */
    171   bool in_list;
    172 
    173   /**
    174    * True if this entry is currently suspended.
    175    */
    176   bool suspended;
    177 
    178 };
    179 
    180 
    181 /**
    182  * Head of linked list over all authorization processes
    183  */
    184 static struct SolveContext *gc_head;
    185 
    186 /**
    187  * Tail of linked list over all authorization processes
    188  */
    189 static struct SolveContext *gc_tail;
    190 
    191 /**
    192  * Task running #do_timeout().
    193  */
    194 static struct GNUNET_SCHEDULER_Task *to_task;
    195 
    196 
    197 /**
    198  * Generate a response telling the client that answering this
    199  * challenge failed because the rate limit has been exceeded.
    200  *
    201  * @param gc request to answer for
    202  * @return MHD status code
    203  */
    204 static enum MHD_Result
    205 reply_rate_limited (const struct SolveContext *gc)
    206 {
    207   if (NULL != gc->authorization)
    208     return TALER_MHD_REPLY_JSON_PACK (
    209       gc->connection,
    210       MHD_HTTP_TOO_MANY_REQUESTS,
    211       TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED),
    212       GNUNET_JSON_pack_uint64 ("request_limit",
    213                                gc->authorization->retry_counter),
    214       GNUNET_JSON_pack_time_rel ("request_frequency",
    215                                  gc->authorization->code_rotation_period));
    216   /* must be security question */
    217   return TALER_MHD_REPLY_JSON_PACK (
    218     gc->connection,
    219     MHD_HTTP_TOO_MANY_REQUESTS,
    220     TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED),
    221     GNUNET_JSON_pack_uint64 ("request_limit",
    222                              INITIAL_RETRY_COUNTER),
    223     GNUNET_JSON_pack_time_rel ("request_frequency",
    224                                MAX_QUESTION_FREQ));
    225 }
    226 
    227 
    228 /**
    229  * Timeout requests that are past their due date.
    230  *
    231  * @param cls NULL
    232  */
    233 static void
    234 do_timeout (void *cls)
    235 {
    236   struct SolveContext *gc;
    237 
    238   (void) cls;
    239   to_task = NULL;
    240   while (NULL !=
    241          (gc = GNUNET_CONTAINER_heap_peek (AH_to_heap)))
    242   {
    243     if (GNUNET_TIME_absolute_is_future (gc->timeout))
    244       break;
    245     if (gc->suspended)
    246     {
    247       /* Test needed as we may have a "concurrent"
    248          wakeup from another task that did not clear
    249          this entry from the heap before the
    250          response process concluded. */
    251       gc->suspended = false;
    252       MHD_resume_connection (gc->connection);
    253     }
    254     GNUNET_assert (NULL != gc->hn);
    255     gc->hn = NULL;
    256     GNUNET_assert (gc ==
    257                    GNUNET_CONTAINER_heap_remove_root (AH_to_heap));
    258   }
    259   if (NULL == gc)
    260     return;
    261   to_task = GNUNET_SCHEDULER_add_at (gc->timeout,
    262                                      &do_timeout,
    263                                      NULL);
    264 }
    265 
    266 
    267 void
    268 AH_truth_solve_shutdown (void)
    269 {
    270   struct SolveContext *gc;
    271 
    272   while (NULL != (gc = gc_head))
    273   {
    274     GNUNET_CONTAINER_DLL_remove (gc_head,
    275                                  gc_tail,
    276                                  gc);
    277     gc->in_list = false;
    278     if (NULL != gc->cpo)
    279     {
    280       TALER_MERCHANT_get_private_order_cancel (gc->cpo);
    281       gc->cpo = NULL;
    282     }
    283     if (NULL != gc->po)
    284     {
    285       TALER_MERCHANT_post_private_orders_cancel (gc->po);
    286       gc->po = NULL;
    287     }
    288     if (gc->suspended)
    289     {
    290       gc->suspended = false;
    291       MHD_resume_connection (gc->connection);
    292     }
    293     if (NULL != gc->as)
    294     {
    295       gc->authorization->cleanup (gc->as);
    296       gc->as = NULL;
    297       gc->authorization = NULL;
    298     }
    299   }
    300   ANASTASIS_authorization_plugin_shutdown ();
    301   if (NULL != to_task)
    302   {
    303     GNUNET_SCHEDULER_cancel (to_task);
    304     to_task = NULL;
    305   }
    306 }
    307 
    308 
    309 /**
    310  * Callback used to notify the application about completed requests.
    311  * Cleans up the requests data structures.
    312  *
    313  * @param[in,out] hc
    314  */
    315 static void
    316 request_done (struct TM_HandlerContext *hc)
    317 {
    318   struct SolveContext *gc = hc->ctx;
    319 
    320   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    321               "Request completed\n");
    322   if (NULL == gc)
    323     return;
    324   hc->cc = NULL;
    325   GNUNET_assert (! gc->suspended);
    326   if (gc->in_list)
    327   {
    328     GNUNET_CONTAINER_DLL_remove (gc_head,
    329                                  gc_tail,
    330                                  gc);
    331     gc->in_list = false;
    332   }
    333   if (NULL != gc->hn)
    334   {
    335     GNUNET_assert (gc ==
    336                    GNUNET_CONTAINER_heap_remove_node (gc->hn));
    337     gc->hn = NULL;
    338   }
    339   if (NULL != gc->as)
    340   {
    341     gc->authorization->cleanup (gc->as);
    342     gc->authorization = NULL;
    343     gc->as = NULL;
    344   }
    345   if (NULL != gc->cpo)
    346   {
    347     TALER_MERCHANT_get_private_order_cancel (gc->cpo);
    348     gc->cpo = NULL;
    349   }
    350   if (NULL != gc->po)
    351   {
    352     TALER_MERCHANT_post_private_orders_cancel (gc->po);
    353     gc->po = NULL;
    354   }
    355   if (NULL != gc->root)
    356   {
    357     json_decref (gc->root);
    358     gc->root = NULL;
    359   }
    360   TALER_MHD_parse_post_cleanup_callback (gc->opaque_post_parsing_context);
    361   GNUNET_free (gc);
    362   hc->ctx = NULL;
    363 }
    364 
    365 
    366 /**
    367  * Transmit a payment request for @a order_id on @a connection
    368  *
    369  * @param gc context to make payment request for
    370  */
    371 static void
    372 make_payment_request (struct SolveContext *gc)
    373 {
    374   struct MHD_Response *resp;
    375 
    376   resp = MHD_create_response_from_buffer (0,
    377                                           NULL,
    378                                           MHD_RESPMEM_PERSISTENT);
    379   GNUNET_assert (NULL != resp);
    380   TALER_MHD_add_global_headers (resp,
    381                                 false);
    382   {
    383     char *hdr;
    384     char *order_id;
    385     const char *pfx;
    386     const char *hn;
    387 
    388     if (0 == strncasecmp ("https://",
    389                           AH_backend_url,
    390                           strlen ("https://")))
    391     {
    392       pfx = "taler://";
    393       hn = &AH_backend_url[strlen ("https://")];
    394     }
    395     else if (0 == strncasecmp ("http://",
    396                                AH_backend_url,
    397                                strlen ("http://")))
    398     {
    399       pfx = "taler+http://";
    400       hn = &AH_backend_url[strlen ("http://")];
    401     }
    402     else
    403     {
    404       /* This invariant holds as per check in anastasis-httpd.c */
    405       GNUNET_assert (0);
    406     }
    407     /* This invariant holds as per check in anastasis-httpd.c */
    408     GNUNET_assert (0 != strlen (hn));
    409 
    410     order_id = GNUNET_STRINGS_data_to_string_alloc (
    411       &gc->payment_identifier,
    412       sizeof (gc->payment_identifier));
    413     GNUNET_asprintf (&hdr,
    414                      "%spay/%s%s/",
    415                      pfx,
    416                      hn,
    417                      order_id);
    418     GNUNET_free (order_id);
    419     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    420                 "Sending payment request `%s'\n",
    421                 hdr);
    422     GNUNET_break (MHD_YES ==
    423                   MHD_add_response_header (resp,
    424                                            ANASTASIS_HTTP_HEADER_TALER,
    425                                            hdr));
    426     GNUNET_free (hdr);
    427   }
    428   gc->resp = resp;
    429   gc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
    430 }
    431 
    432 
    433 /**
    434  * Callbacks of this type are used to serve the result of submitting a
    435  * /contract request to a merchant.
    436  *
    437  * @param cls our `struct SolveContext`
    438  * @param por response details
    439  */
    440 static void
    441 proposal_cb (struct SolveContext *gc,
    442              const struct TALER_MERCHANT_PostPrivateOrdersResponse *por)
    443 {
    444   enum GNUNET_DB_QueryStatus qs;
    445 
    446   gc->po = NULL;
    447   GNUNET_assert (gc->in_list);
    448   GNUNET_CONTAINER_DLL_remove (gc_head,
    449                                gc_tail,
    450                                gc);
    451   gc->in_list = false;
    452   GNUNET_assert (gc->suspended);
    453   gc->suspended = false;
    454   MHD_resume_connection (gc->connection);
    455   AH_trigger_daemon (NULL);
    456   if (MHD_HTTP_OK != por->hr.http_status)
    457   {
    458     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    459                 "Backend returned status %u/%d\n",
    460                 por->hr.http_status,
    461                 (int) por->hr.ec);
    462     GNUNET_break (0);
    463     gc->resp = TALER_MHD_MAKE_JSON_PACK (
    464       GNUNET_JSON_pack_uint64 ("code",
    465                                TALER_EC_ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR),
    466       GNUNET_JSON_pack_string ("hint",
    467                                "Failed to setup order with merchant backend"),
    468       GNUNET_JSON_pack_uint64 ("backend-ec",
    469                                por->hr.ec),
    470       GNUNET_JSON_pack_uint64 ("backend-http-status",
    471                                por->hr.http_status),
    472       GNUNET_JSON_pack_allow_null (
    473         GNUNET_JSON_pack_object_steal ("backend-reply",
    474                                        (json_t *) por->hr.reply)));
    475     gc->response_code = MHD_HTTP_BAD_GATEWAY;
    476     return;
    477   }
    478   qs = db->record_challenge_payment (db->cls,
    479                                      &gc->truth_uuid,
    480                                      &gc->payment_identifier,
    481                                      &gc->challenge_cost);
    482   if (0 >= qs)
    483   {
    484     GNUNET_break (0);
    485     gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
    486                                      "record challenge payment");
    487     gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    488     return;
    489   }
    490   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    491               "Setup fresh order, creating payment request\n");
    492   make_payment_request (gc);
    493 }
    494 
    495 
    496 /**
    497  * Callback to process a GET /check-payment request
    498  *
    499  * @param cls our `struct SolveContext`
    500  * @param osr order status
    501  */
    502 static void
    503 check_payment_cb (struct SolveContext *gc,
    504                   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    505 
    506 {
    507   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    508 
    509   gc->cpo = NULL;
    510   GNUNET_assert (gc->in_list);
    511   GNUNET_CONTAINER_DLL_remove (gc_head,
    512                                gc_tail,
    513                                gc);
    514   gc->in_list = false;
    515   GNUNET_assert (gc->suspended);
    516   gc->suspended = false;
    517   MHD_resume_connection (gc->connection);
    518   AH_trigger_daemon (NULL);
    519 
    520   switch (hr->http_status)
    521   {
    522   case MHD_HTTP_OK:
    523     GNUNET_assert (NULL != osr);
    524     break;
    525   case MHD_HTTP_NOT_FOUND:
    526     /* We created this order before, how can it be not found now? */
    527     GNUNET_break (0);
    528     gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_TRUTH_ORDER_DISAPPEARED,
    529                                      NULL);
    530     gc->response_code = MHD_HTTP_BAD_GATEWAY;
    531     return;
    532   case MHD_HTTP_BAD_GATEWAY:
    533     gc->resp = TALER_MHD_make_error (
    534       TALER_EC_ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD,
    535       NULL);
    536     gc->response_code = MHD_HTTP_BAD_GATEWAY;
    537     return;
    538   case MHD_HTTP_GATEWAY_TIMEOUT:
    539     gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT,
    540                                      "Timeout check payment status");
    541     GNUNET_assert (NULL != gc->resp);
    542     gc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
    543     return;
    544   default:
    545     {
    546       char status[14];
    547 
    548       GNUNET_snprintf (status,
    549                        sizeof (status),
    550                        "%u",
    551                        hr->http_status);
    552       gc->resp = TALER_MHD_make_error (
    553         TALER_EC_ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS,
    554         status);
    555       GNUNET_assert (NULL != gc->resp);
    556       gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    557       return;
    558     }
    559   }
    560 
    561   GNUNET_assert (MHD_HTTP_OK == hr->http_status);
    562   switch (osr->details.ok.status)
    563   {
    564   case TALER_MERCHANT_OSC_PAID:
    565     {
    566       enum GNUNET_DB_QueryStatus qs;
    567 
    568       qs = db->update_challenge_payment (db->cls,
    569                                          &gc->truth_uuid,
    570                                          &gc->payment_identifier);
    571       if (0 <= qs)
    572       {
    573         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    574                     "Order has been paid, continuing with request processing\n")
    575         ;
    576         return; /* continue as planned */
    577       }
    578       GNUNET_break (0);
    579       gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
    580                                        "update challenge payment");
    581       gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    582       return; /* continue as planned */
    583     }
    584   case TALER_MERCHANT_OSC_CLAIMED:
    585   case TALER_MERCHANT_OSC_UNPAID:
    586     /* repeat payment request */
    587     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    588                 "Order remains unpaid, sending payment request again\n");
    589     make_payment_request (gc);
    590     return;
    591   }
    592   /* should never get here */
    593   GNUNET_break (0);
    594 }
    595 
    596 
    597 /**
    598  * Helper function used to ask our backend to begin processing a
    599  * payment for the user's account.  May perform asynchronous
    600  * operations by suspending the connection if required.
    601  *
    602  * @param gc context to begin payment for.
    603  * @return MHD status code
    604  */
    605 static enum MHD_Result
    606 begin_payment (struct SolveContext *gc)
    607 {
    608   enum GNUNET_DB_QueryStatus qs;
    609   char *order_id;
    610 
    611   qs = db->lookup_challenge_payment (db->cls,
    612                                      &gc->truth_uuid,
    613                                      &gc->payment_identifier);
    614   if (qs < 0)
    615   {
    616     GNUNET_break (0);
    617     return TALER_MHD_reply_with_error (gc->connection,
    618                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    619                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    620                                        "lookup challenge payment");
    621   }
    622   GNUNET_assert (! gc->in_list);
    623   gc->in_list = true;
    624   GNUNET_CONTAINER_DLL_insert (gc_tail,
    625                                gc_head,
    626                                gc);
    627   GNUNET_assert (! gc->suspended);
    628   gc->suspended = true;
    629   MHD_suspend_connection (gc->connection);
    630   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    631   {
    632     /* We already created the order, check if it was paid */
    633     struct GNUNET_TIME_Relative timeout;
    634 
    635     order_id = GNUNET_STRINGS_data_to_string_alloc (
    636       &gc->payment_identifier,
    637       sizeof (gc->payment_identifier));
    638     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    639                 "Order exists, checking payment status for order `%s'\n",
    640                 order_id);
    641     timeout = GNUNET_TIME_absolute_get_remaining (gc->timeout);
    642     gc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx,
    643                                                        AH_backend_url,
    644                                                        order_id);
    645     GNUNET_assert (NULL != gc->cpo);
    646     GNUNET_assert (
    647       GNUNET_OK ==
    648       TALER_MERCHANT_get_private_order_set_options (
    649         gc->cpo,
    650         TALER_MERCHANT_get_private_order_option_timeout (timeout)));
    651     GNUNET_assert (TALER_EC_NONE ==
    652                    TALER_MERCHANT_get_private_order_start (gc->cpo,
    653                                                            &check_payment_cb,
    654                                                            gc));
    655   }
    656   else
    657   {
    658     /* Create a fresh order */
    659     struct GNUNET_TIME_Timestamp pay_deadline;
    660 
    661     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    662                                 &gc->payment_identifier,
    663                                 sizeof (struct ANASTASIS_PaymentSecretP));
    664     order_id = GNUNET_STRINGS_data_to_string_alloc (
    665       &gc->payment_identifier,
    666       sizeof (gc->payment_identifier));
    667     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    668                 "Creating fresh order `%s'\n",
    669                 order_id);
    670     pay_deadline = GNUNET_TIME_relative_to_timestamp (
    671       ANASTASIS_CHALLENGE_OFFER_LIFETIME);
    672     {
    673       json_t *order;
    674 
    675       order = GNUNET_JSON_PACK (
    676         TALER_JSON_pack_amount ("amount",
    677                                 &gc->challenge_cost),
    678         GNUNET_JSON_pack_string ("summary",
    679                                  "challenge fee for anastasis service"),
    680         GNUNET_JSON_pack_string ("order_id",
    681                                  order_id),
    682         GNUNET_JSON_pack_time_rel ("auto_refund",
    683                                    AUTO_REFUND_TIMEOUT),
    684         GNUNET_JSON_pack_timestamp ("pay_deadline",
    685                                     pay_deadline));
    686       gc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx,
    687                                                           AH_backend_url,
    688                                                           order);
    689       json_decref (order);
    690     }
    691     GNUNET_assert (
    692       GNUNET_OK ==
    693       TALER_MERCHANT_post_private_orders_set_options (
    694         gc->po,
    695         TALER_MERCHANT_post_private_orders_option_create_token (false),
    696         TALER_MERCHANT_post_private_orders_option_refund_delay (
    697           AUTO_REFUND_TIMEOUT)));
    698     GNUNET_assert (TALER_EC_NONE ==
    699                    TALER_MERCHANT_post_private_orders_start (gc->po,
    700                                                              &proposal_cb,
    701                                                              gc));
    702   }
    703   GNUNET_free (order_id);
    704   AH_trigger_curl ();
    705   return MHD_YES;
    706 }
    707 
    708 
    709 /**
    710  * Load encrypted keyshare from db and return it to the client.
    711  *
    712  * @param truth_uuid UUID to the truth for the looup
    713  * @param connection the connection to respond upon
    714  * @return MHD status code
    715  */
    716 static enum MHD_Result
    717 return_key_share (
    718   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    719   struct MHD_Connection *connection)
    720 {
    721   struct ANASTASIS_CRYPTO_EncryptedKeyShareP encrypted_keyshare;
    722 
    723   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    724               "Returning key share of %s\n",
    725               TALER_B2S (truth_uuid));
    726   {
    727     enum GNUNET_DB_QueryStatus qs;
    728 
    729     qs = db->get_key_share (db->cls,
    730                             truth_uuid,
    731                             &encrypted_keyshare);
    732     switch (qs)
    733     {
    734     case GNUNET_DB_STATUS_HARD_ERROR:
    735     case GNUNET_DB_STATUS_SOFT_ERROR:
    736       GNUNET_break (0);
    737       return TALER_MHD_reply_with_error (connection,
    738                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    739                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    740                                          "get key share");
    741     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    742       /* this should be "impossible", after all the
    743          client was able to solve the challenge!
    744          (Exception: we deleted the truth via GC
    745          just while the client was trying to recover.
    746          Alas, highly unlikely...) */
    747       GNUNET_break (0);
    748       return TALER_MHD_reply_with_error (connection,
    749                                          MHD_HTTP_NOT_FOUND,
    750                                          TALER_EC_ANASTASIS_TRUTH_KEY_SHARE_GONE,
    751                                          NULL);
    752     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    753       break;
    754     }
    755   }
    756 
    757   {
    758     struct MHD_Response *resp;
    759     enum MHD_Result ret;
    760 
    761     resp = MHD_create_response_from_buffer (sizeof (encrypted_keyshare),
    762                                             &encrypted_keyshare,
    763                                             MHD_RESPMEM_MUST_COPY);
    764     TALER_MHD_add_global_headers (resp,
    765                                   false);
    766     ret = MHD_queue_response (connection,
    767                               MHD_HTTP_OK,
    768                               resp);
    769     MHD_destroy_response (resp);
    770     return ret;
    771   }
    772 }
    773 
    774 
    775 /**
    776  * Mark @a gc as suspended and update the respective
    777  * data structures and jobs.
    778  *
    779  * @param[in,out] gc context of the suspended operation
    780  */
    781 static void
    782 gc_suspended (struct SolveContext *gc)
    783 {
    784   GNUNET_assert (NULL == gc->hn);
    785   GNUNET_assert (! gc->suspended);
    786   gc->suspended = true;
    787   if (NULL == AH_to_heap)
    788     AH_to_heap = GNUNET_CONTAINER_heap_create (
    789       GNUNET_CONTAINER_HEAP_ORDER_MIN);
    790   gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
    791                                          gc,
    792                                          gc->timeout.abs_value_us);
    793   if (NULL != to_task)
    794   {
    795     GNUNET_SCHEDULER_cancel (to_task);
    796     to_task = NULL;
    797   }
    798   {
    799     struct SolveContext *rn;
    800 
    801     rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
    802     to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
    803                                        &do_timeout,
    804                                        NULL);
    805   }
    806 }
    807 
    808 
    809 /**
    810  * Run the authorization method-specific 'process' function and continue
    811  * based on its result with generating an HTTP response.
    812  *
    813  * @param connection the connection we are handling
    814  * @param gc our overall handler context
    815  */
    816 static enum MHD_Result
    817 run_authorization_process (struct MHD_Connection *connection,
    818                            struct SolveContext *gc)
    819 {
    820   enum ANASTASIS_AUTHORIZATION_SolveResult ret;
    821 
    822   GNUNET_assert (! gc->suspended);
    823   if (NULL == gc->authorization->solve)
    824   {
    825     GNUNET_break (0);
    826     return TALER_MHD_reply_with_error (gc->connection,
    827                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    828                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
    829                                        "solve method not implemented for authorization method");
    830   }
    831   ret = gc->authorization->solve (gc->as,
    832                                   gc->timeout,
    833                                   &gc->challenge_response,
    834                                   connection);
    835   switch (ret)
    836   {
    837   case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED:
    838     /* connection was suspended */
    839     gc_suspended (gc);
    840     return MHD_YES;
    841   case ANASTASIS_AUTHORIZATION_SRES_FAILED:
    842     gc->authorization->cleanup (gc->as);
    843     gc->as = NULL;
    844     return MHD_YES;
    845   case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED:
    846     gc->authorization->cleanup (gc->as);
    847     gc->as = NULL;
    848     return MHD_NO;
    849   case ANASTASIS_AUTHORIZATION_SRES_FINISHED:
    850     GNUNET_assert (! gc->suspended);
    851     gc->authorization->cleanup (gc->as);
    852     gc->as = NULL;
    853     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    854                 "Resuming with authorization successful!\n");
    855     if (gc->in_list)
    856     {
    857       GNUNET_CONTAINER_DLL_remove (gc_head,
    858                                    gc_tail,
    859                                    gc);
    860       gc->in_list = false;
    861     }
    862     return MHD_YES;
    863   }
    864   GNUNET_break (0);
    865   return MHD_NO;
    866 }
    867 
    868 
    869 /**
    870  * Use the database to rate-limit queries to the authentication
    871  * procedure, but without actually storing 'real' challenge codes.
    872  *
    873  * @param[in,out] gc context to rate limit requests for
    874  * @return #GNUNET_OK if rate-limiting passes,
    875  *         #GNUNET_NO if a reply was sent (rate limited)
    876  *         #GNUNET_SYSERR if we failed and no reply
    877  *                        was queued
    878  */
    879 static enum GNUNET_GenericReturnValue
    880 rate_limit (struct SolveContext *gc)
    881 {
    882   enum GNUNET_DB_QueryStatus qs;
    883   struct GNUNET_TIME_Timestamp rt;
    884   uint64_t code;
    885   enum ANASTASIS_DB_CodeStatus cs;
    886   struct GNUNET_HashCode hc;
    887   bool satisfied;
    888   uint64_t dummy;
    889 
    890   rt = GNUNET_TIME_UNIT_FOREVER_TS;
    891   qs = db->create_challenge_code (db->cls,
    892                                   &gc->truth_uuid,
    893                                   MAX_QUESTION_FREQ,
    894                                   GNUNET_TIME_UNIT_HOURS,
    895                                   INITIAL_RETRY_COUNTER,
    896                                   &rt,
    897                                   &code);
    898   if (0 > qs)
    899   {
    900     GNUNET_break (0 < qs);
    901     return (MHD_YES ==
    902             TALER_MHD_reply_with_error (gc->connection,
    903                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    904                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    905                                         "create_challenge_code (for rate limiting)"))
    906            ? GNUNET_NO
    907            : GNUNET_SYSERR;
    908   }
    909   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    910   {
    911     return (MHD_YES ==
    912             reply_rate_limited (gc))
    913            ? GNUNET_NO
    914            : GNUNET_SYSERR;
    915   }
    916   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    917               "Using intentionally wrong answer to produce rate-limiting\n");
    918   /* decrement trial counter */
    919   ANASTASIS_hash_answer (code + 1,      /* always use wrong answer */
    920                          &hc);
    921   cs = db->verify_challenge_code (db->cls,
    922                                   &gc->truth_uuid,
    923                                   &hc,
    924                                   &dummy,
    925                                   &satisfied);
    926   switch (cs)
    927   {
    928   case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
    929     /* good, what we wanted */
    930     return GNUNET_OK;
    931   case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
    932   case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
    933     GNUNET_break (0);
    934     return (MHD_YES ==
    935             TALER_MHD_reply_with_error (gc->connection,
    936                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    937                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    938                                         "verify_challenge_code"))
    939            ? GNUNET_NO
    940            : GNUNET_SYSERR;
    941   case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
    942     return (MHD_YES ==
    943             reply_rate_limited (gc))
    944            ? GNUNET_NO
    945            : GNUNET_SYSERR;
    946   case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
    947     /* this should be impossible, we used code+1 */
    948     GNUNET_assert (0);
    949   }
    950   return GNUNET_SYSERR;
    951 }
    952 
    953 
    954 /**
    955  * Handle special case of a security question where we do not
    956  * generate a code. Rate limits answers against brute forcing.
    957  *
    958  * @param[in,out] gc request to handle
    959  * @param decrypted_truth hash to check against
    960  * @param decrypted_truth_size number of bytes in @a decrypted_truth
    961  * @return MHD status code
    962  */
    963 static enum MHD_Result
    964 handle_security_question (struct SolveContext *gc,
    965                           const void *decrypted_truth,
    966                           size_t decrypted_truth_size)
    967 {
    968   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    969               "Handling security question challenge\n");
    970   /* rate limit */
    971   {
    972     enum GNUNET_GenericReturnValue ret;
    973 
    974     ret = rate_limit (gc);
    975     if (GNUNET_OK != ret)
    976       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    977   }
    978   /* check reply matches truth */
    979   if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
    980        (0 != memcmp (&gc->challenge_response,
    981                      decrypted_truth,
    982                      decrypted_truth_size)) )
    983   {
    984     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    985                 "Wrong answer provided to secure question had %u bytes, wanted %u\n",
    986                 (unsigned int) decrypted_truth_size,
    987                 (unsigned int) sizeof (struct GNUNET_HashCode));
    988     return TALER_MHD_reply_with_error (gc->connection,
    989                                        MHD_HTTP_FORBIDDEN,
    990                                        TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
    991                                        NULL);
    992   }
    993   /* good, return the key share */
    994   return return_key_share (&gc->truth_uuid,
    995                            gc->connection);
    996 }
    997 
    998 
    999 /**
   1000  * Handle special case of an answer being directly checked by the
   1001  * plugin and not by our database.  Also ensures that the
   1002  * request is rate-limited.
   1003  *
   1004  * @param[in,out] gc request to handle
   1005  * @param decrypted_truth hash to check against
   1006  * @param decrypted_truth_size number of bytes in @a decrypted_truth
   1007  * @return MHD status code
   1008  */
   1009 static enum MHD_Result
   1010 direct_validation (struct SolveContext *gc,
   1011                    const void *decrypted_truth,
   1012                    size_t decrypted_truth_size)
   1013 {
   1014   /* Non-random code, call plugin directly! */
   1015   enum ANASTASIS_AUTHORIZATION_SolveResult aar;
   1016   enum GNUNET_GenericReturnValue ret;
   1017 
   1018   ret = rate_limit (gc);
   1019   if (GNUNET_OK != ret)
   1020     return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
   1021   gc->as = gc->authorization->start (gc->authorization->cls,
   1022                                      &AH_trigger_daemon,
   1023                                      NULL,
   1024                                      &gc->truth_uuid,
   1025                                      0LLU,
   1026                                      decrypted_truth,
   1027                                      decrypted_truth_size);
   1028   if (NULL == gc->as)
   1029   {
   1030     GNUNET_break (0);
   1031     return TALER_MHD_reply_with_error (gc->connection,
   1032                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1033                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1034                                        NULL);
   1035   }
   1036   if (NULL == gc->authorization->solve)
   1037   {
   1038     GNUNET_break (0);
   1039     return TALER_MHD_reply_with_error (gc->connection,
   1040                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1041                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1042                                        "solve method not implemented for authorization method");
   1043   }
   1044   aar = gc->authorization->solve (gc->as,
   1045                                   gc->timeout,
   1046                                   &gc->challenge_response,
   1047                                   gc->connection);
   1048   switch (aar)
   1049   {
   1050   case ANASTASIS_AUTHORIZATION_SRES_FAILED:
   1051     return MHD_YES;
   1052   case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED:
   1053     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1054                 "Suspending request handling\n");
   1055     gc_suspended (gc);
   1056     return MHD_YES;
   1057   case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED:
   1058     return MHD_NO;
   1059   case ANASTASIS_AUTHORIZATION_SRES_FINISHED:
   1060     return return_key_share (&gc->truth_uuid,
   1061                              gc->connection);
   1062   }
   1063   GNUNET_break (0);
   1064   return MHD_NO;
   1065 }
   1066 
   1067 
   1068 /**
   1069  * Handle special case of an answer being checked
   1070  * by the plugin asynchronously (IBAN) after we inverted
   1071  * the hash using the database.
   1072  *
   1073  * @param[in,out] gc request to handle
   1074  * @param code validation code provided by the client
   1075  * @param decrypted_truth hash to check against
   1076  * @param decrypted_truth_size number of bytes in @a decrypted_truth
   1077  * @return MHD status code
   1078  */
   1079 static enum MHD_Result
   1080 iban_validation (struct SolveContext *gc,
   1081                  uint64_t code,
   1082                  const void *decrypted_truth,
   1083                  size_t decrypted_truth_size)
   1084 {
   1085   enum ANASTASIS_AUTHORIZATION_SolveResult aar;
   1086 
   1087   gc->as = gc->authorization->start (gc->authorization->cls,
   1088                                      &AH_trigger_daemon,
   1089                                      NULL,
   1090                                      &gc->truth_uuid,
   1091                                      code,
   1092                                      decrypted_truth,
   1093                                      decrypted_truth_size);
   1094   if (NULL == gc->as)
   1095   {
   1096     GNUNET_break (0);
   1097     return TALER_MHD_reply_with_error (gc->connection,
   1098                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1099                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1100                                        NULL);
   1101   }
   1102   if (NULL == gc->authorization->solve)
   1103   {
   1104     GNUNET_break (0);
   1105     return TALER_MHD_reply_with_error (gc->connection,
   1106                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1107                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1108                                        "solve method not implemented for authorization method");
   1109   }
   1110   aar = gc->authorization->solve (gc->as,
   1111                                   gc->timeout,
   1112                                   &gc->challenge_response,
   1113                                   gc->connection);
   1114   switch (aar)
   1115   {
   1116   case ANASTASIS_AUTHORIZATION_SRES_FAILED:
   1117     return MHD_YES;
   1118   case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED:
   1119     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1120                 "Suspending request handling\n");
   1121     gc_suspended (gc);
   1122     return MHD_YES;
   1123   case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED:
   1124     return MHD_NO;
   1125   case ANASTASIS_AUTHORIZATION_SRES_FINISHED:
   1126     return return_key_share (&gc->truth_uuid,
   1127                              gc->connection);
   1128   }
   1129   GNUNET_break (0);
   1130   return MHD_NO;
   1131 }
   1132 
   1133 
   1134 enum MHD_Result
   1135 AH_handler_truth_solve (
   1136   struct MHD_Connection *connection,
   1137   struct TM_HandlerContext *hc,
   1138   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
   1139   const char *upload_data,
   1140   size_t *upload_data_size)
   1141 {
   1142   struct SolveContext *gc = hc->ctx;
   1143   void *encrypted_truth;
   1144   size_t encrypted_truth_size;
   1145   void *decrypted_truth;
   1146   size_t decrypted_truth_size;
   1147   char *truth_mime = NULL;
   1148   bool is_question;
   1149 
   1150   if (NULL == gc)
   1151   {
   1152     /* Fresh request, do initial setup */
   1153     gc = GNUNET_new (struct SolveContext);
   1154     gc->hc = hc;
   1155     hc->ctx = gc;
   1156     gc->connection = connection;
   1157     gc->truth_uuid = *truth_uuid;
   1158     gc->hc->cc = &request_done;
   1159     gc->timeout = GNUNET_TIME_relative_to_absolute (
   1160       GNUNET_TIME_UNIT_SECONDS);
   1161     TALER_MHD_parse_request_timeout (connection,
   1162                                      &gc->timeout);
   1163   } /* end of first-time initialization (if NULL == gc) */
   1164   else
   1165   {
   1166     /* might have been woken up by authorization plugin,
   1167        so clear the flag. MDH called us, so we are
   1168        clearly no longer suspended */
   1169     gc->suspended = false;
   1170     if (NULL != gc->resp)
   1171     {
   1172       enum MHD_Result ret;
   1173 
   1174       /* We generated a response asynchronously, queue that */
   1175       ret = MHD_queue_response (connection,
   1176                                 gc->response_code,
   1177                                 gc->resp);
   1178       GNUNET_break (MHD_YES == ret);
   1179       MHD_destroy_response (gc->resp);
   1180       gc->resp = NULL;
   1181       return ret;
   1182     }
   1183     if (NULL != gc->as)
   1184     {
   1185       /* Authorization process is "running", check what is going on */
   1186       GNUNET_assert (NULL != gc->authorization);
   1187       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1188                   "Continuing with running the authorization process\n");
   1189       GNUNET_assert (! gc->suspended);
   1190       return run_authorization_process (connection,
   1191                                         gc);
   1192     }
   1193     /* We get here if the async check for payment said this request
   1194        was indeed paid! */
   1195   }
   1196 
   1197   if (NULL == gc->root)
   1198   {
   1199     /* parse byte stream upload into JSON */
   1200     enum GNUNET_GenericReturnValue res;
   1201 
   1202     res = TALER_MHD_parse_post_json (connection,
   1203                                      &gc->opaque_post_parsing_context,
   1204                                      upload_data,
   1205                                      upload_data_size,
   1206                                      &gc->root);
   1207     if (GNUNET_SYSERR == res)
   1208     {
   1209       GNUNET_assert (NULL == gc->root);
   1210       return MHD_NO; /* bad upload, could not even generate error */
   1211     }
   1212     if ( (GNUNET_NO == res) ||
   1213          (NULL == gc->root) )
   1214     {
   1215       GNUNET_assert (NULL == gc->root);
   1216       return MHD_YES; /* so far incomplete upload or parser error */
   1217     }
   1218 
   1219     /* 'root' is now initialized, parse JSON body */
   1220     {
   1221       struct GNUNET_JSON_Specification spec[] = {
   1222         GNUNET_JSON_spec_fixed_auto ("truth_decryption_key",
   1223                                      &gc->truth_key),
   1224         GNUNET_JSON_spec_fixed_auto ("h_response",
   1225                                      &gc->challenge_response),
   1226         GNUNET_JSON_spec_mark_optional (
   1227           GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1228                                        &gc->payment_identifier),
   1229           &gc->no_payment_identifier_provided),
   1230         GNUNET_JSON_spec_end ()
   1231       };
   1232       enum GNUNET_GenericReturnValue pres;
   1233 
   1234       pres = TALER_MHD_parse_json_data (connection,
   1235                                         gc->root,
   1236                                         spec);
   1237       if (GNUNET_SYSERR == pres)
   1238       {
   1239         GNUNET_break (0);
   1240         return MHD_NO; /* hard failure */
   1241       }
   1242       if (GNUNET_NO == pres)
   1243       {
   1244         GNUNET_break_op (0);
   1245         return MHD_YES; /* failure */
   1246       }
   1247       if (! gc->no_payment_identifier_provided)
   1248         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1249                     "Client provided payment identifier `%s'\n",
   1250                     TALER_B2S (&gc->payment_identifier));
   1251     }
   1252   }
   1253 
   1254   {
   1255     /* load encrypted truth from DB; we may do this repeatedly
   1256        while handling the same request, if payment was checked
   1257        asynchronously! */
   1258     enum GNUNET_DB_QueryStatus qs;
   1259     char *method;
   1260 
   1261     qs = db->get_escrow_challenge (db->cls,
   1262                                    &gc->truth_uuid,
   1263                                    &encrypted_truth,
   1264                                    &encrypted_truth_size,
   1265                                    &truth_mime,
   1266                                    &method);
   1267     switch (qs)
   1268     {
   1269     case GNUNET_DB_STATUS_HARD_ERROR:
   1270     case GNUNET_DB_STATUS_SOFT_ERROR:
   1271       GNUNET_break (0);
   1272       return TALER_MHD_reply_with_error (gc->connection,
   1273                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1274                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1275                                          "get escrow challenge");
   1276     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1277       return TALER_MHD_reply_with_error (connection,
   1278                                          MHD_HTTP_NOT_FOUND,
   1279                                          TALER_EC_ANASTASIS_TRUTH_UNKNOWN,
   1280                                          NULL);
   1281     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1282       break;
   1283     }
   1284     is_question = (0 == strcmp ("question",
   1285                                 method));
   1286     if (! is_question)
   1287     {
   1288       gc->authorization
   1289         = ANASTASIS_authorization_plugin_load (method,
   1290                                                db,
   1291                                                AH_cfg);
   1292       if (NULL == gc->authorization)
   1293       {
   1294         enum MHD_Result ret;
   1295 
   1296         ret = TALER_MHD_reply_with_error (
   1297           connection,
   1298           MHD_HTTP_INTERNAL_SERVER_ERROR,
   1299           TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED,
   1300           method);
   1301         GNUNET_free (encrypted_truth);
   1302         GNUNET_free (truth_mime);
   1303         GNUNET_free (method);
   1304         return ret;
   1305       }
   1306       gc->challenge_cost = gc->authorization->cost;
   1307     }
   1308     else
   1309     {
   1310       gc->challenge_cost = AH_question_cost;
   1311     }
   1312     GNUNET_free (method);
   1313   }
   1314 
   1315   /* check for payment */
   1316   if ( (is_question) ||
   1317        (! gc->authorization->payment_plugin_managed) )
   1318   {
   1319     if (! TALER_amount_is_zero (&gc->challenge_cost))
   1320     {
   1321       /* Check database to see if the transaction is paid for */
   1322       enum GNUNET_DB_QueryStatus qs;
   1323       bool paid;
   1324 
   1325       if (gc->no_payment_identifier_provided)
   1326       {
   1327         GNUNET_free (truth_mime);
   1328         GNUNET_free (encrypted_truth);
   1329         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1330                     "Beginning payment, client did not provide payment identifier\n");
   1331         return begin_payment (gc);
   1332       }
   1333       qs = db->check_challenge_payment (db->cls,
   1334                                         &gc->payment_identifier,
   1335                                         &gc->truth_uuid,
   1336                                         &paid);
   1337       switch (qs)
   1338       {
   1339       case GNUNET_DB_STATUS_HARD_ERROR:
   1340       case GNUNET_DB_STATUS_SOFT_ERROR:
   1341         GNUNET_break (0);
   1342         GNUNET_free (truth_mime);
   1343         GNUNET_free (encrypted_truth);
   1344         return TALER_MHD_reply_with_error (gc->connection,
   1345                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
   1346                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
   1347                                            "check challenge payment");
   1348       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1349         /* Create fresh payment identifier (cannot trust client) */
   1350         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1351                     "Client-provided payment identifier is unknown.\n");
   1352         GNUNET_free (truth_mime);
   1353         GNUNET_free (encrypted_truth);
   1354         return begin_payment (gc);
   1355       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1356         if (! paid)
   1357         {
   1358           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1359                       "Payment identifier known. Checking payment with client's payment identifier\n");
   1360           GNUNET_free (truth_mime);
   1361           GNUNET_free (encrypted_truth);
   1362           return begin_payment (gc);
   1363         }
   1364         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1365                     "Payment confirmed\n");
   1366         break;
   1367       }
   1368     }
   1369     else
   1370     {
   1371       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1372                   "Request is free of charge\n");
   1373     }
   1374   }
   1375 
   1376   /* We've been paid, now validate the response */
   1377   /* decrypt encrypted_truth */
   1378   ANASTASIS_CRYPTO_truth_decrypt (&gc->truth_key,
   1379                                   encrypted_truth,
   1380                                   encrypted_truth_size,
   1381                                   &decrypted_truth,
   1382                                   &decrypted_truth_size);
   1383   GNUNET_free (encrypted_truth);
   1384   if (NULL == decrypted_truth)
   1385   {
   1386     /* most likely, the decryption key is simply wrong */
   1387     GNUNET_break_op (0);
   1388     GNUNET_free (truth_mime);
   1389     return TALER_MHD_reply_with_error (connection,
   1390                                        MHD_HTTP_BAD_REQUEST,
   1391                                        TALER_EC_ANASTASIS_TRUTH_DECRYPTION_FAILED,
   1392                                        NULL);
   1393   }
   1394 
   1395   /* Special case for secure question: we do not generate a numeric challenge,
   1396      but check that the hash matches */
   1397   if (is_question)
   1398   {
   1399     enum MHD_Result ret;
   1400 
   1401     ret = handle_security_question (gc,
   1402                                     decrypted_truth,
   1403                                     decrypted_truth_size);
   1404     GNUNET_free (truth_mime);
   1405     GNUNET_free (decrypted_truth);
   1406     return ret;
   1407   }
   1408 
   1409   /* Not security question, check for answer in DB */
   1410   {
   1411     enum ANASTASIS_DB_CodeStatus cs;
   1412     bool satisfied = false;
   1413     uint64_t code;
   1414 
   1415     GNUNET_free (truth_mime);
   1416     if (gc->authorization->user_provided_code)
   1417     {
   1418       enum MHD_Result res;
   1419 
   1420       if (GNUNET_TIME_absolute_is_past (gc->timeout))
   1421       {
   1422         GNUNET_free (decrypted_truth);
   1423         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1424                     "Timeout with user provided code\n");
   1425         return TALER_MHD_reply_with_error (connection,
   1426                                            MHD_HTTP_FORBIDDEN,
   1427                                            TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER,
   1428                                            "timeout awaiting validation");
   1429       }
   1430       res = direct_validation (gc,
   1431                                decrypted_truth,
   1432                                decrypted_truth_size);
   1433       GNUNET_free (decrypted_truth);
   1434       return res;
   1435     }
   1436 
   1437     /* random code, check against database */
   1438     cs = db->verify_challenge_code (db->cls,
   1439                                     &gc->truth_uuid,
   1440                                     &gc->challenge_response,
   1441                                     &code,
   1442                                     &satisfied);
   1443     switch (cs)
   1444     {
   1445     case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
   1446       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1447                   "Provided response does not match our stored challenge\n");
   1448       GNUNET_free (decrypted_truth);
   1449       return TALER_MHD_reply_with_error (connection,
   1450                                          MHD_HTTP_FORBIDDEN,
   1451                                          TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
   1452                                          NULL);
   1453     case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
   1454     case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
   1455       GNUNET_break (0);
   1456       GNUNET_free (decrypted_truth);
   1457       return TALER_MHD_reply_with_error (gc->connection,
   1458                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1459                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1460                                          "verify_challenge_code");
   1461     case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
   1462       GNUNET_free (decrypted_truth);
   1463       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1464                   "Specified challenge code %s was not issued\n",
   1465                   GNUNET_h2s (&gc->challenge_response));
   1466       return TALER_MHD_reply_with_error (connection,
   1467                                          MHD_HTTP_FORBIDDEN,
   1468                                          TALER_EC_ANASTASIS_TRUTH_CHALLENGE_UNKNOWN,
   1469                                          "specific challenge code was not issued");
   1470     case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
   1471       if (! satisfied)
   1472       {
   1473         enum MHD_Result res;
   1474 
   1475         res = iban_validation (gc,
   1476                                code,
   1477                                decrypted_truth,
   1478                                decrypted_truth_size);
   1479         GNUNET_free (decrypted_truth);
   1480         return res;
   1481       }
   1482       GNUNET_free (decrypted_truth);
   1483       return return_key_share (&gc->truth_uuid,
   1484                                connection);
   1485     default:
   1486       GNUNET_break (0);
   1487       return MHD_NO;
   1488     }
   1489   }
   1490 }