anastasis

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

testing_cmd_challenge_answer.c (16762B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020, 2021, 2022 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU 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 General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file testing/testing_cmd_challenge_answer.c
     18  * @brief command to execute the anastasis recovery service
     19  * @author Christian Grothoff
     20  * @author Dennis Neufeld
     21  * @author Dominik Meister
     22  */
     23 #include "platform.h"
     24 #include "anastasis_testing_lib.h"
     25 #include <taler/taler_util.h>
     26 #include <taler/taler_testing_lib.h>
     27 #include <taler/taler_merchant_service.h>
     28 
     29 
     30 // FIXME: break up into two files, one for start, one for answer!
     31 
     32 /**
     33  * State for a "challenge answer" CMD.
     34  */
     35 struct ChallengeState
     36 {
     37   /**
     38    * The interpreter state.
     39    */
     40   struct TALER_TESTING_Interpreter *is;
     41 
     42   /**
     43    * Reference to the challenge we are solving
     44    */
     45   struct ANASTASIS_Challenge *c;
     46 
     47   /**
     48    * Answer to the challenge we are solving
     49    */
     50   const char *answer;
     51 
     52   /**
     53    * Reference to the recovery process
     54    */
     55   const char *challenge_ref;
     56 
     57   /**
     58    * Reference to the payment
     59    */
     60   const char *payment_ref;
     61 
     62   /**
     63    * "taler://pay/" URL we got back, if any. Otherwise NULL.
     64    */
     65   char *payment_uri;
     66 
     67   /**
     68    * Order ID extracted from @e payment_uri, or NULL.
     69    */
     70   char *order_id;
     71 
     72   /**
     73    * Payment order ID we are to provide in the request.
     74    */
     75   struct ANASTASIS_PaymentSecretP payment_order_req;
     76 
     77   /**
     78    * Expected answer status code.
     79    */
     80   enum ANASTASIS_ChallengeAnswerStatus expected_acs;
     81 
     82   /**
     83    * Expected start status code.
     84    */
     85   enum ANASTASIS_ChallengeStartStatus expected_scs;
     86 
     87   /**
     88    * Index of the challenge we are solving
     89    */
     90   unsigned int challenge_index;
     91 
     92   /**
     93    * 0 for no plugin needed 1 for plugin needed to authenticate
     94    */
     95   unsigned int mode;
     96 
     97   /**
     98    * code we read in the file generated by the plugin
     99    */
    100   char *code;
    101 
    102 };
    103 
    104 
    105 static void
    106 challenge_answer_cb (void *af_cls,
    107                      const struct ANASTASIS_ChallengeAnswerResponse *csr)
    108 {
    109   struct ChallengeState *cs = af_cls;
    110 
    111   cs->c = NULL;
    112   if (csr->cs != cs->expected_acs)
    113   {
    114     GNUNET_break (0);
    115     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    116                 "Expected status %u, got %u\n",
    117                 cs->expected_acs,
    118                 csr->cs);
    119     TALER_TESTING_interpreter_fail (cs->is);
    120     return;
    121   }
    122   switch (csr->cs)
    123   {
    124   case ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED:
    125     break;
    126   case ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER:
    127     break;
    128   case ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED:
    129     if (0 != strncmp (csr->details.payment_required.taler_pay_uri,
    130                       "taler+http://pay/",
    131                       strlen ("taler+http://pay/")))
    132     {
    133       GNUNET_break (0);
    134       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    135                   "Invalid payment URI `%s'\n",
    136                   csr->details.payment_required.taler_pay_uri);
    137       TALER_TESTING_interpreter_fail (cs->is);
    138       return;
    139     }
    140     cs->payment_uri = GNUNET_strdup (
    141       csr->details.payment_required.taler_pay_uri);
    142     {
    143       struct TALER_MERCHANT_PayUriData pud;
    144 
    145       if (GNUNET_OK !=
    146           TALER_MERCHANT_parse_pay_uri (cs->payment_uri,
    147                                         &pud))
    148       {
    149         GNUNET_break (0);
    150         TALER_TESTING_interpreter_fail (cs->is);
    151         return;
    152       }
    153       cs->order_id = GNUNET_strdup (pud.order_id);
    154       if (GNUNET_OK !=
    155           GNUNET_STRINGS_string_to_data (cs->order_id,
    156                                          strlen (cs->order_id),
    157                                          &cs->payment_order_req,
    158                                          sizeof (cs->payment_order_req)))
    159       {
    160         GNUNET_break (0);
    161         TALER_TESTING_interpreter_fail (cs->is);
    162         return;
    163       }
    164       TALER_MERCHANT_parse_pay_uri_free (&pud);
    165     }
    166     TALER_TESTING_interpreter_next (cs->is);
    167     return;
    168   case ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN:
    169     break;
    170   case ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE:
    171     GNUNET_break (0);
    172     TALER_TESTING_interpreter_fail (cs->is);
    173     return;
    174   case ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED:
    175     break;
    176   }
    177   TALER_TESTING_interpreter_next (cs->is);
    178 }
    179 
    180 
    181 /**
    182  * Run a "recover secret" CMD.
    183  *
    184  * @param cls closure.
    185  * @param cmd command currently being run.
    186  * @param is interpreter state.
    187  */
    188 static void
    189 challenge_answer_run (void *cls,
    190                       const struct TALER_TESTING_Command *cmd,
    191                       struct TALER_TESTING_Interpreter *is)
    192 {
    193   struct ChallengeState *cs = cls;
    194   const struct ANASTASIS_Challenge **c;
    195   const struct ANASTASIS_PaymentSecretP *ps;
    196 
    197   cs->is = is;
    198   if (NULL != cs->challenge_ref)
    199   {
    200     const struct TALER_TESTING_Command *ref;
    201 
    202     ref = TALER_TESTING_interpreter_lookup_command (
    203       is,
    204       cs->challenge_ref);
    205     if (NULL == ref)
    206     {
    207       GNUNET_break (0);
    208       TALER_TESTING_interpreter_fail (cs->is);
    209       return;
    210     }
    211     if (GNUNET_OK !=
    212         ANASTASIS_TESTING_get_trait_challenges (ref,
    213                                                 cs->challenge_index,
    214                                                 &c))
    215     {
    216       GNUNET_break (0);
    217       TALER_TESTING_interpreter_fail (cs->is);
    218       return;
    219     }
    220     cs->c = (struct ANASTASIS_Challenge *) *c;
    221   }
    222 
    223   if (NULL != cs->payment_ref)
    224   {
    225     const struct TALER_TESTING_Command *ref;
    226 
    227     ref = TALER_TESTING_interpreter_lookup_command (is,
    228                                                     cs->payment_ref);
    229     if (NULL == ref)
    230     {
    231       GNUNET_break (0);
    232       TALER_TESTING_interpreter_fail (cs->is);
    233       return;
    234     }
    235     if (GNUNET_OK !=
    236         ANASTASIS_TESTING_get_trait_payment_secret (ref,
    237                                                     &ps))
    238     {
    239       GNUNET_break (0);
    240       TALER_TESTING_interpreter_fail (cs->is);
    241       return;
    242     }
    243   }
    244   else
    245   {
    246     ps = NULL;
    247   }
    248 
    249   if (1 == cs->mode)
    250   {
    251     const struct TALER_TESTING_Command *ref;
    252     const char *answer;
    253     unsigned long long code;
    254     char dummy;
    255 
    256     ref = TALER_TESTING_interpreter_lookup_command (is,
    257                                                     cs->answer);
    258     if (NULL == ref)
    259     {
    260       GNUNET_break (0);
    261       TALER_TESTING_interpreter_fail (cs->is);
    262       return;
    263     }
    264     if (GNUNET_OK !=
    265         ANASTASIS_TESTING_get_trait_code (ref,
    266                                           &answer))
    267     {
    268       GNUNET_break (0);
    269       TALER_TESTING_interpreter_fail (cs->is);
    270       return;
    271     }
    272     if (1 !=
    273         sscanf (answer,
    274                 "%llu%c",
    275                 &code,
    276                 &dummy))
    277     {
    278       GNUNET_break (0);
    279       TALER_TESTING_interpreter_fail (cs->is);
    280       return;
    281     }
    282     if (GNUNET_OK !=
    283         ANASTASIS_challenge_answer2 (cs->c,
    284                                      ps,
    285                                      GNUNET_TIME_UNIT_ZERO,
    286                                      code,
    287                                      &challenge_answer_cb,
    288                                      cs))
    289     {
    290       GNUNET_break (0);
    291       cs->c = NULL;
    292       TALER_TESTING_interpreter_fail (cs->is);
    293       return;
    294     }
    295 
    296   }
    297   else
    298   {
    299     if (GNUNET_OK !=
    300         ANASTASIS_challenge_answer (cs->c,
    301                                     ps,
    302                                     GNUNET_TIME_UNIT_ZERO,
    303                                     cs->answer,
    304                                     &challenge_answer_cb,
    305                                     cs))
    306     {
    307       GNUNET_break (0);
    308       cs->c = NULL;
    309       TALER_TESTING_interpreter_fail (cs->is);
    310       return;
    311     }
    312   }
    313 }
    314 
    315 
    316 static void
    317 challenge_start_cb (void *af_cls,
    318                     const struct ANASTASIS_ChallengeStartResponse *csr)
    319 {
    320   struct ChallengeState *cs = af_cls;
    321 
    322   cs->c = NULL;
    323   if (csr->cs != cs->expected_scs)
    324   {
    325     GNUNET_break (0);
    326     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    327                 "Expected status %u, got %u\n",
    328                 cs->expected_scs,
    329                 csr->cs);
    330     TALER_TESTING_interpreter_fail (cs->is);
    331     return;
    332   }
    333   switch (csr->cs)
    334   {
    335   case ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED:
    336     {
    337       FILE *file;
    338       char code[22];
    339 
    340       file = fopen (csr->details.tan_filename,
    341                     "r");
    342       if (NULL == file)
    343       {
    344         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    345                                   "open",
    346                                   csr->details.tan_filename);
    347         TALER_TESTING_interpreter_fail (cs->is);
    348         return;
    349       }
    350       /* fscanf returns EOF (-1), not 0, on an empty/short file; checking
    351          `0 ==` misses that and would use the uninitialized `code` buffer. */
    352       if (1 != fscanf (file,
    353                        "%21s",
    354                        code))
    355       {
    356         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    357                                   "fscanf",
    358                                   csr->details.tan_filename);
    359         GNUNET_break (0 == fclose (file));
    360         TALER_TESTING_interpreter_fail (cs->is);
    361         return;
    362       }
    363       GNUNET_break (0 == fclose (file));
    364       cs->code = GNUNET_strdup (code);
    365       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    366                   "Read code `%s'\n",
    367                   code);
    368     }
    369     break;
    370   case ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED:
    371     GNUNET_break (0); /* FIXME: not implemented */
    372     break;
    373   case ANASTASIS_CHALLENGE_START_STATUS_TAN_ALREADY_SENT:
    374     GNUNET_break (0); /* FIXME: not implemented */
    375     break;
    376   case ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED:
    377     GNUNET_break (0); /* FIXME: not implemented */
    378     break;
    379   case ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED:
    380     if (0 != strncmp (csr->details.payment_required.taler_pay_uri,
    381                       "taler+http://pay/",
    382                       strlen ("taler+http://pay/")))
    383     {
    384       GNUNET_break (0);
    385       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    386                   "Invalid payment URI `%s'\n",
    387                   csr->details.payment_required.taler_pay_uri);
    388       TALER_TESTING_interpreter_fail (cs->is);
    389       return;
    390     }
    391     cs->payment_uri = GNUNET_strdup (
    392       csr->details.payment_required.taler_pay_uri);
    393     {
    394       struct TALER_MERCHANT_PayUriData pud;
    395 
    396       if (GNUNET_OK !=
    397           TALER_MERCHANT_parse_pay_uri (cs->payment_uri,
    398                                         &pud))
    399       {
    400         GNUNET_break (0);
    401         TALER_TESTING_interpreter_fail (cs->is);
    402         return;
    403       }
    404       cs->order_id = GNUNET_strdup (pud.order_id);
    405       if (GNUNET_OK !=
    406           GNUNET_STRINGS_string_to_data (cs->order_id,
    407                                          strlen (cs->order_id),
    408                                          &cs->payment_order_req,
    409                                          sizeof (cs->payment_order_req)))
    410       {
    411         GNUNET_break (0);
    412         TALER_TESTING_interpreter_fail (cs->is);
    413         return;
    414       }
    415       TALER_MERCHANT_parse_pay_uri_free (&pud);
    416     }
    417     TALER_TESTING_interpreter_next (cs->is);
    418     return;
    419   case ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN:
    420     break;
    421   case ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE:
    422     GNUNET_break (0);
    423     TALER_TESTING_interpreter_fail (cs->is);
    424     return;
    425   }
    426   TALER_TESTING_interpreter_next (cs->is);
    427 }
    428 
    429 
    430 /**
    431  * Run a "recover secret" CMD.
    432  *
    433  * @param cls closure.
    434  * @param cmd command currently being run.
    435  * @param is interpreter state.
    436  */
    437 static void
    438 challenge_start_run (void *cls,
    439                      const struct TALER_TESTING_Command *cmd,
    440                      struct TALER_TESTING_Interpreter *is)
    441 {
    442   struct ChallengeState *cs = cls;
    443   const struct ANASTASIS_Challenge **c;
    444   const struct ANASTASIS_PaymentSecretP *ps;
    445 
    446   cs->is = is;
    447   {
    448     const struct TALER_TESTING_Command *ref;
    449 
    450     ref = TALER_TESTING_interpreter_lookup_command (
    451       is,
    452       cs->challenge_ref);
    453     if (NULL == ref)
    454     {
    455       GNUNET_break (0);
    456       TALER_TESTING_interpreter_fail (cs->is);
    457       return;
    458     }
    459     if (GNUNET_OK !=
    460         ANASTASIS_TESTING_get_trait_challenges (ref,
    461                                                 cs->challenge_index,
    462                                                 &c))
    463     {
    464       GNUNET_break (0);
    465       TALER_TESTING_interpreter_fail (cs->is);
    466       return;
    467     }
    468   }
    469 
    470   if (NULL != cs->payment_ref)
    471   {
    472     const struct TALER_TESTING_Command *pref;
    473 
    474     pref = TALER_TESTING_interpreter_lookup_command (is,
    475                                                      cs->payment_ref);
    476     if (NULL == pref)
    477     {
    478       GNUNET_break (0);
    479       TALER_TESTING_interpreter_fail (cs->is);
    480       return;
    481     }
    482     if (GNUNET_OK !=
    483         ANASTASIS_TESTING_get_trait_payment_secret (pref,
    484                                                     &ps))
    485     {
    486       GNUNET_break (0);
    487       TALER_TESTING_interpreter_fail (cs->is);
    488       return;
    489     }
    490   }
    491   else
    492   {
    493     ps = NULL;
    494   }
    495   if (GNUNET_OK !=
    496       ANASTASIS_challenge_start ((struct ANASTASIS_Challenge *) *c,
    497                                  ps,
    498                                  &challenge_start_cb,
    499                                  cs))
    500   {
    501     GNUNET_break (0);
    502     TALER_TESTING_interpreter_fail (cs->is);
    503     return;
    504   }
    505 }
    506 
    507 
    508 /**
    509  * Free the state of a "recover secret" CMD, and possibly
    510  * cancel it if it did not complete.
    511  *
    512  * @param cls closure.
    513  * @param cmd command being freed.
    514  */
    515 static void
    516 challenge_cleanup (void *cls,
    517                    const struct TALER_TESTING_Command *cmd)
    518 {
    519   struct ChallengeState *cs = cls;
    520 
    521   if (NULL != cs->c)
    522   {
    523     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    524                 "Command '%s' did not complete (challenge answer)\n",
    525                 cmd->label);
    526     ANASTASIS_challenge_abort (cs->c);
    527     cs->c = NULL;
    528   }
    529   GNUNET_free (cs->payment_uri);
    530   GNUNET_free (cs->order_id);
    531   GNUNET_free (cs->code);
    532   GNUNET_free (cs);
    533 }
    534 
    535 
    536 /**
    537  * Offer internal data to other commands.
    538  *
    539  * @param cls closure
    540  * @param[out] ret result (could be anything)
    541  * @param trait name of the trait
    542  * @param index index number of the object to extract.
    543  * @return #GNUNET_OK on success
    544  */
    545 static enum GNUNET_GenericReturnValue
    546 challenge_create_traits (void *cls,
    547                          const void **ret,
    548                          const char *trait,
    549                          unsigned int index)
    550 {
    551   struct ChallengeState *cs = cls;
    552   struct TALER_TESTING_Trait traits[] = {
    553     ANASTASIS_TESTING_make_trait_code (cs->code),
    554     ANASTASIS_TESTING_make_trait_payment_secret (
    555       &cs->payment_order_req),
    556     TALER_TESTING_make_trait_taler_uri (cs->payment_uri),
    557     TALER_TESTING_make_trait_order_id (cs->order_id),
    558     TALER_TESTING_trait_end ()
    559   };
    560 
    561   return TALER_TESTING_get_trait (traits,
    562                                   ret,
    563                                   trait,
    564                                   index);
    565 }
    566 
    567 
    568 struct TALER_TESTING_Command
    569 ANASTASIS_TESTING_cmd_challenge_start (
    570   const char *label,
    571   const char *payment_ref,
    572   const char *challenge_ref,
    573   unsigned int challenge_index,
    574   enum ANASTASIS_ChallengeStartStatus expected_cs)
    575 {
    576   struct ChallengeState *cs;
    577 
    578   cs = GNUNET_new (struct ChallengeState);
    579   cs->expected_scs = expected_cs;
    580   cs->challenge_ref = challenge_ref;
    581   cs->payment_ref = payment_ref;
    582   cs->challenge_index = challenge_index;
    583   {
    584     struct TALER_TESTING_Command cmd = {
    585       .cls = cs,
    586       .label = label,
    587       .run = &challenge_start_run,
    588       .cleanup = &challenge_cleanup,
    589       .traits = &challenge_create_traits
    590     };
    591 
    592     return cmd;
    593   }
    594 }
    595 
    596 
    597 struct TALER_TESTING_Command
    598 ANASTASIS_TESTING_cmd_challenge_answer (
    599   const char *label,
    600   const char *payment_ref,
    601   const char *challenge_ref,
    602   unsigned int challenge_index,
    603   const char *answer,
    604   unsigned int mode,
    605   enum ANASTASIS_ChallengeAnswerStatus expected_cs)
    606 {
    607   struct ChallengeState *cs;
    608 
    609   cs = GNUNET_new (struct ChallengeState);
    610   cs->expected_acs = expected_cs;
    611   cs->challenge_ref = challenge_ref;
    612   cs->payment_ref = payment_ref;
    613   cs->answer = answer;
    614   cs->challenge_index = challenge_index;
    615   cs->mode = mode;
    616   {
    617     struct TALER_TESTING_Command cmd = {
    618       .cls = cs,
    619       .label = label,
    620       .run = &challenge_answer_run,
    621       .cleanup = &challenge_cleanup,
    622       .traits = &challenge_create_traits
    623     };
    624 
    625     return cmd;
    626   }
    627 }
    628 
    629 
    630 /* end of testing_cmd_challenge_answer.c */