anastasis

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

testing_cmd_recover_secret.c (12253B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020, 2021 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_recover_secret.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 
     28 
     29 /**
     30  * State for a "recover secret" CMD.
     31  */
     32 struct RecoverSecretState
     33 {
     34   /**
     35    * The interpreter state.
     36    */
     37   struct TALER_TESTING_Interpreter *is;
     38 
     39   /**
     40    * URL of the anastasis backend.
     41    */
     42   const char *anastasis_url;
     43 
     44   /**
     45    * The /policy GET operation handle.
     46    */
     47   struct ANASTASIS_Recovery *recovery;
     48 
     49   /**
     50    * Reference to download command we expect to look up.
     51    */
     52   const char *download_reference;
     53 
     54   /**
     55    * Reference to core secret share we expect to look up.
     56    */
     57   const char *core_secret_reference;
     58 
     59   /**
     60    * Options for how we are supposed to do the download.
     61    */
     62   enum ANASTASIS_TESTING_RecoverSecretOption rsopt;
     63 
     64   /**
     65    * Identification data from the user
     66    */
     67   json_t *id_data;
     68 
     69   /**
     70    * Recovery information from the lookup
     71    */
     72   struct ANASTASIS_RecoveryInformation *ri;
     73 
     74   /**
     75    * Coresecret to check if decryption worked
     76    */
     77   const void *core_secret;
     78 
     79   /**
     80    * Task scheduled to wait for recovery to complete.
     81    */
     82   struct GNUNET_SCHEDULER_Task *recovery_task;
     83 
     84   /**
     85    * version of the recovery document
     86    */
     87   unsigned int version;
     88 
     89   /**
     90    * #GNUNET_OK if the secret was recovered, #GNUNET_SYSERR if
     91    * recovery failed (yielded wrong secret).
     92    */
     93   int recovered;
     94 };
     95 
     96 
     97 /**
     98  * Callback which passes back the recovery document and its possible
     99  * policies. Also passes back the version of the document for the user
    100  * to check.
    101  *
    102  * @param cls closure for the callback
    103  * @param ri recovery information struct which contains the policies
    104  */
    105 static void
    106 policy_lookup_cb (void *cls,
    107                   const struct ANASTASIS_RecoveryInformation *ri)
    108 {
    109   struct RecoverSecretState *rss = cls;
    110 
    111   if (NULL == ri)
    112   {
    113     GNUNET_break (0);
    114     TALER_TESTING_interpreter_fail (rss->is);
    115     return;
    116   }
    117   if (0 == ri->cs_len)
    118   {
    119     GNUNET_break (0);
    120     TALER_TESTING_interpreter_fail (rss->is);
    121     return;
    122   }
    123   rss->ri = (struct ANASTASIS_RecoveryInformation *) ri;
    124   TALER_TESTING_interpreter_next (rss->is);
    125 }
    126 
    127 
    128 /**
    129  * This function is called whenever the recovery process ends.
    130  * On success, the secret is returned in @a secret.
    131  *
    132  * @param cls closure
    133  * @param rc error code
    134  * @param secret contains the core secret which is passed to the user
    135  * @param secret_size defines the size of the core secret
    136  */
    137 static void
    138 core_secret_cb (void *cls,
    139                 enum ANASTASIS_RecoveryStatus rc,
    140                 const void *secret,
    141                 size_t secret_size)
    142 {
    143   struct RecoverSecretState *rss = cls;
    144 
    145   rss->recovery = NULL;
    146   if (ANASTASIS_RS_SUCCESS != rc)
    147   {
    148     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    149                 "Recovery failed with status %d\n",
    150                 rc);
    151     TALER_TESTING_interpreter_fail (rss->is);
    152     return;
    153   }
    154   if (0 != memcmp (secret,
    155                    rss->core_secret,
    156                    secret_size))
    157   {
    158     GNUNET_break (0);
    159     rss->recovered = GNUNET_SYSERR;
    160     if (NULL != rss->recovery_task)
    161     {
    162       GNUNET_SCHEDULER_cancel (rss->recovery_task);
    163       rss->recovery_task = NULL;
    164       TALER_TESTING_interpreter_fail (rss->is);
    165     }
    166     return;
    167   }
    168   rss->recovered = GNUNET_OK;
    169   if (NULL != rss->recovery_task)
    170   {
    171     GNUNET_SCHEDULER_cancel (rss->recovery_task);
    172     rss->recovery_task = NULL;
    173     TALER_TESTING_interpreter_next (rss->is);
    174   }
    175 }
    176 
    177 
    178 /**
    179  * Run a "recover secret" CMD.
    180  *
    181  * @param cls closure.
    182  * @param cmd command currently being run.
    183  * @param is interpreter state.
    184  */
    185 static void
    186 recover_secret_run (void *cls,
    187                     const struct TALER_TESTING_Command *cmd,
    188                     struct TALER_TESTING_Interpreter *is)
    189 {
    190   struct RecoverSecretState *rss = cls;
    191   const struct TALER_TESTING_Command *ref;
    192   const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt;
    193   rss->is = is;
    194 
    195   GNUNET_assert (NULL != rss->download_reference);
    196   ref = TALER_TESTING_interpreter_lookup_command (
    197     is,
    198     rss->download_reference);
    199   if (NULL == ref)
    200   {
    201     GNUNET_break (0);
    202     TALER_TESTING_interpreter_fail (rss->is);
    203     return;
    204   }
    205   if (GNUNET_OK !=
    206       ANASTASIS_TESTING_get_trait_provider_salt (ref,
    207                                                  &provider_salt))
    208   {
    209     GNUNET_break (0);
    210     TALER_TESTING_interpreter_fail (rss->is);
    211     return;
    212   }
    213   if (NULL != rss->core_secret_reference)
    214   {
    215     ref = TALER_TESTING_interpreter_lookup_command (
    216       is,
    217       rss->core_secret_reference);
    218     if (NULL == ref)
    219     {
    220       GNUNET_break (0);
    221       TALER_TESTING_interpreter_fail (rss->is);
    222       return;
    223     }
    224     if (GNUNET_OK !=
    225         ANASTASIS_TESTING_get_trait_core_secret (
    226           ref,
    227           &rss->core_secret))
    228     {
    229       GNUNET_break (0);
    230       TALER_TESTING_interpreter_fail (rss->is);
    231       return;
    232     }
    233   }
    234   rss->recovery = ANASTASIS_recovery_begin (
    235     TALER_TESTING_interpreter_get_context (is),
    236     rss->id_data,
    237     rss->version,
    238     rss->anastasis_url,
    239     provider_salt,
    240     &policy_lookup_cb,
    241     rss,
    242     &core_secret_cb,
    243     rss);
    244   if (NULL == rss->recovery)
    245   {
    246     GNUNET_break (0);
    247     TALER_TESTING_interpreter_fail (rss->is);
    248     return;
    249   }
    250 }
    251 
    252 
    253 /**
    254  * Task to run the abort routine on the given @a cls object
    255  * after the stack has fully unwound.
    256  *
    257  * @param cls a `struct ANASTASIS_Recovery *`
    258  */
    259 static void
    260 delayed_abort (void *cls)
    261 {
    262   struct ANASTASIS_Recovery *recovery = cls;
    263 
    264   ANASTASIS_recovery_abort (recovery);
    265 }
    266 
    267 
    268 /**
    269  * Free the state of a "recover secret" CMD, and possibly
    270  * cancel it if it did not complete.
    271  *
    272  * @param cls closure
    273  * @param cmd command being freed.
    274  */
    275 static void
    276 recover_secret_cleanup (void *cls,
    277                         const struct TALER_TESTING_Command *cmd)
    278 {
    279   struct RecoverSecretState *rss = cls;
    280 
    281   if (NULL != rss->recovery)
    282   {
    283     /* must run first, or at least before #core_secret_cb */
    284     (void) GNUNET_SCHEDULER_add_with_priority (
    285       GNUNET_SCHEDULER_PRIORITY_SHUTDOWN,
    286       &delayed_abort,
    287       rss->recovery);
    288     rss->recovery = NULL;
    289   }
    290   if (NULL != rss->recovery_task)
    291   {
    292     GNUNET_SCHEDULER_cancel (rss->recovery_task);
    293     rss->recovery_task = NULL;
    294   }
    295   json_decref (rss->id_data);
    296   GNUNET_free (rss);
    297 }
    298 
    299 
    300 /**
    301  * Offer internal data to other commands.
    302  *
    303  * @param cls closure
    304  * @param[out] ret result (could be anything)
    305  * @param trait name of the trait
    306  * @param index index number of the object to extract.
    307  * @return #GNUNET_OK on success
    308  */
    309 static enum GNUNET_GenericReturnValue
    310 recover_secret_traits (void *cls,
    311                        const void **ret,
    312                        const char *trait,
    313                        unsigned int index)
    314 {
    315   struct RecoverSecretState *rss = cls;
    316 
    317   if (NULL == rss->ri)
    318   {
    319     GNUNET_break (0);
    320     return GNUNET_SYSERR;
    321   }
    322   if (index >= rss->ri->cs_len)
    323   {
    324     GNUNET_break (0);
    325     return GNUNET_SYSERR;
    326   }
    327   {
    328     struct TALER_TESTING_Trait traits[] = {
    329       ANASTASIS_TESTING_make_trait_challenges (
    330         index,
    331         (const struct ANASTASIS_Challenge **) &rss->ri->cs[index]),
    332       TALER_TESTING_trait_end ()
    333     };
    334 
    335     return TALER_TESTING_get_trait (traits,
    336                                     ret,
    337                                     trait,
    338                                     index);
    339   }
    340 }
    341 
    342 
    343 /**
    344  * Function called on timeout of the secret finishing operation.
    345  *
    346  * @param cls a `struct RecoverSecretState *`
    347  */
    348 static void
    349 recovery_fail (void *cls)
    350 {
    351   struct RecoverSecretState *rss = cls;
    352 
    353   rss->recovery_task = NULL;
    354   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    355               "Timeout during secret recovery\n");
    356   TALER_TESTING_interpreter_fail (rss->is);
    357 }
    358 
    359 
    360 /**
    361  * Wait @a delay for @a cmd to finish secret recovery.
    362  *
    363  * @param cmd command to wait on
    364  * @param delay how long to wait at most
    365  */
    366 static void
    367 recover_secret_finish (struct TALER_TESTING_Command *cmd,
    368                        struct GNUNET_TIME_Relative delay)
    369 {
    370   struct RecoverSecretState *rss = cmd->cls;
    371 
    372   GNUNET_assert (&recover_secret_run == cmd->run);
    373   GNUNET_assert (NULL == rss->recovery_task);
    374   switch (rss->recovered)
    375   {
    376   case GNUNET_OK:
    377     TALER_TESTING_interpreter_next (rss->is);
    378     break;
    379   case GNUNET_NO:
    380     rss->recovery_task = GNUNET_SCHEDULER_add_delayed (delay,
    381                                                        &recovery_fail,
    382                                                        rss);
    383     break;
    384   case GNUNET_SYSERR:
    385     TALER_TESTING_interpreter_fail (rss->is);
    386     break;
    387   }
    388 }
    389 
    390 
    391 struct TALER_TESTING_Command
    392 ANASTASIS_TESTING_cmd_recover_secret (
    393   const char *label,
    394   const char *anastasis_url,
    395   const json_t *id_data,
    396   unsigned int version,
    397   enum ANASTASIS_TESTING_RecoverSecretOption rso,
    398   const char *download_ref,
    399   const char *core_secret_ref)
    400 {
    401   struct RecoverSecretState *rss;
    402 
    403   rss = GNUNET_new (struct RecoverSecretState);
    404   rss->version = version;
    405   rss->id_data = json_incref ((json_t *) id_data);
    406   rss->rsopt = rso;
    407   rss->anastasis_url = anastasis_url;
    408   rss->download_reference = download_ref;
    409   rss->core_secret_reference = core_secret_ref;
    410   {
    411     struct TALER_TESTING_Command cmd = {
    412       .cls = rss,
    413       .label = label,
    414       .run = &recover_secret_run,
    415       .cleanup = &recover_secret_cleanup,
    416       .traits = &recover_secret_traits
    417     };
    418 
    419     return cmd;
    420   }
    421 }
    422 
    423 
    424 /**
    425  * State for a "recover secret finish" CMD.
    426  */
    427 struct RecoverSecretFinishState
    428 {
    429   /**
    430    * The interpreter state.
    431    */
    432   struct TALER_TESTING_Interpreter *is;
    433 
    434   /**
    435    * URL of the anastasis backend.
    436    */
    437   const char *recover_label;
    438 
    439   /**
    440    * Timeout.
    441    */
    442   struct GNUNET_TIME_Relative timeout;
    443 
    444 };
    445 
    446 
    447 /**
    448  * Run a "recover secret finish" CMD.
    449  *
    450  * @param cls closure.
    451  * @param cmd command currently being run.
    452  * @param is interpreter state.
    453  */
    454 static void
    455 recover_secret_finish_run (void *cls,
    456                            const struct TALER_TESTING_Command *cmd,
    457                            struct TALER_TESTING_Interpreter *is)
    458 {
    459   struct RecoverSecretFinishState *rsfs = cls;
    460   struct TALER_TESTING_Command *ref;
    461 
    462   rsfs->is = is;
    463   ref = (struct TALER_TESTING_Command *)
    464         TALER_TESTING_interpreter_lookup_command (is,
    465                                                   rsfs->recover_label);
    466   if (NULL == ref)
    467   {
    468     GNUNET_break (0);
    469     TALER_TESTING_interpreter_fail (rsfs->is);
    470     return;
    471   }
    472   recover_secret_finish (ref,
    473                          rsfs->timeout);
    474 }
    475 
    476 
    477 /**
    478  * Free the state of a "recover secret finish" CMD, and possibly
    479  * cancel it if it did not complete.
    480  *
    481  * @param cls closure
    482  * @param cmd command being freed.
    483  */
    484 static void
    485 recover_secret_finish_cleanup (void *cls,
    486                                const struct TALER_TESTING_Command *cmd)
    487 {
    488   struct RecoverSecretFinishState *rsfs = cls;
    489 
    490   GNUNET_free (rsfs);
    491 }
    492 
    493 
    494 struct TALER_TESTING_Command
    495 ANASTASIS_TESTING_cmd_recover_secret_finish (
    496   const char *label,
    497   const char *recover_label,
    498   struct GNUNET_TIME_Relative timeout)
    499 {
    500   struct RecoverSecretFinishState *rsfs;
    501 
    502   rsfs = GNUNET_new (struct RecoverSecretFinishState);
    503   rsfs->recover_label = recover_label;
    504   rsfs->timeout = timeout;
    505   {
    506     struct TALER_TESTING_Command cmd = {
    507       .cls = rsfs,
    508       .label = label,
    509       .run = &recover_secret_finish_run,
    510       .cleanup = &recover_secret_finish_cleanup
    511     };
    512 
    513     return cmd;
    514   }
    515 }
    516 
    517 
    518 /* end of testing_cmd_recover_secret.c */