anastasis

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

anastasis-httpd_truth-solve.c (48668B)


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