anastasis

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

test_anastasis_db.c (14799B)


      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 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.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis/test_anastasis_db.c
     18  * @brief testcase for anastasis postgres db plugin
     19  * @author Marcello Stanisci
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include "anastasis_database_lib.h"
     24 #include "anastasis_util_lib.h"
     25 #include <gnunet/gnunet_signatures.h>
     26 
     27 
     28 #define FAILIF(cond)                            \
     29         do {                                          \
     30           if (! (cond)) { break;}                       \
     31           GNUNET_break (0);                           \
     32           goto drop;                                     \
     33         } while (0)
     34 
     35 #define RND_BLK(ptr)                                                    \
     36         GNUNET_CRYPTO_random_block (ptr, sizeof (* \
     37                                                  ptr))
     38 
     39 /**
     40  * Global return value for the test.  Initially -1, set to 0 upon
     41  * completion.   Other values indicate some kind of error.
     42  */
     43 static int result;
     44 
     45 /**
     46  * Main function that will be run by the scheduler.
     47  *
     48  * @param cls closure with config
     49  */
     50 static void
     51 run (void *cls)
     52 {
     53   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
     54   struct TALER_Amount amount;
     55   struct ANASTASIS_PaymentSecretP paymentSecretP;
     56   struct ANASTASIS_CRYPTO_AccountPublicKeyP accountPubP;
     57   struct ANASTASIS_AccountSignatureP accountSig;
     58   struct ANASTASIS_AccountSignatureP res_account_sig;
     59   struct GNUNET_HashCode recoveryDataHash;
     60   struct GNUNET_HashCode res_recovery_data_hash;
     61   struct GNUNET_HashCode r;
     62   struct GNUNET_TIME_Relative rel_time;
     63   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     64   struct ANASTASIS_CRYPTO_EncryptedKeyShareP key_share;
     65   unsigned int post_counter;
     66   const char *mime_type;
     67   const char *method;
     68   uint32_t docVersion;
     69   uint32_t res_version;
     70   size_t recoverydatasize;
     71   void *res_recovery_data = NULL;
     72   struct ANASTASIS_CRYPTO_EncryptedKeyShareP res_key_share;
     73   bool paid;
     74   struct GNUNET_TIME_Timestamp pi_creation_date;
     75   bool valid_counter;
     76   uint32_t recversion = 1;
     77   unsigned char aes_gcm_tag[16];
     78   const char *recovery_data = "RECOVERY_DATA";
     79   uint64_t challenge_code = 1234;
     80   struct GNUNET_HashCode c_hash;
     81   struct ANASTASIS_UploadSignaturePS usp = {
     82     .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
     83     .purpose.size = htonl (sizeof (usp))
     84   };
     85 
     86   if (GNUNET_OK !=
     87       ANASTASIS_DB_init (cfg))
     88   {
     89     result = 77;
     90     return;
     91   }
     92   (void) ANASTASIS_DB_drop_tables ();
     93   if (GNUNET_OK !=
     94       ANASTASIS_DB_create_tables ())
     95   {
     96     result = 77;
     97     return;
     98   }
     99   if (GNUNET_OK !=
    100       ANASTASIS_DB_preflight ())
    101   {
    102     result = 77;
    103     return;
    104   }
    105 
    106   GNUNET_CRYPTO_hash (recovery_data,
    107                       strlen (recovery_data),
    108                       &recoveryDataHash);
    109   RND_BLK (&paymentSecretP);
    110   RND_BLK (&aes_gcm_tag);
    111   post_counter = 2;
    112   mime_type = "Picture";
    113   method = "Method";
    114   TALER_string_to_amount ("EUR:30",&amount);
    115 
    116   GNUNET_CRYPTO_random_block (&truth_uuid,
    117                               sizeof (truth_uuid));
    118   rel_time = GNUNET_TIME_UNIT_MONTHS;
    119 
    120   GNUNET_assert (GNUNET_OK ==
    121                  TALER_string_to_amount ("EUR:1",
    122                                          &amount));
    123 
    124   memset (&key_share, 1, sizeof (key_share));
    125   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    126           ANASTASIS_DB_insert_truth (
    127             &truth_uuid,
    128             &key_share,
    129             mime_type,
    130             "encrypted_truth",
    131             strlen ("encrypted_truth"),
    132             method,
    133             rel_time));
    134 
    135   /* A TOTP time step may be consumed at most once, so that a code cannot be
    136      replayed for as long as its window lasts. */
    137   {
    138     struct ANASTASIS_CRYPTO_TruthUUIDP unknown_uuid;
    139 
    140     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    141             ANASTASIS_DB_update_totp_counter (&truth_uuid,
    142                                               1000));
    143     /* the same step again is the replay we are guarding against */
    144     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    145             ANASTASIS_DB_update_totp_counter (&truth_uuid,
    146                                               1000));
    147     /* an earlier step, as a client with a slow clock would send */
    148     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    149             ANASTASIS_DB_update_totp_counter (&truth_uuid,
    150                                               999));
    151     /* the next step is a fresh code and must still work */
    152     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    153             ANASTASIS_DB_update_totp_counter (&truth_uuid,
    154                                               1001));
    155     memset (&unknown_uuid, 42, sizeof (unknown_uuid));
    156     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    157             ANASTASIS_DB_update_totp_counter (&unknown_uuid,
    158                                               1));
    159   }
    160 
    161   memset (&accountPubP, 2, sizeof (accountPubP));
    162   memset (&accountSig, 3, sizeof (accountSig));
    163   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    164           ANASTASIS_DB_get_recdoc_payment (
    165             &accountPubP,
    166             &paymentSecretP,
    167             &paid,
    168             &valid_counter,
    169             &pi_creation_date));
    170 
    171   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    172           ANASTASIS_DB_do_insert_recdoc_payment (
    173             &accountPubP,
    174             post_counter,
    175             &paymentSecretP,
    176             &amount));
    177   {
    178     struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    179     struct GNUNET_TIME_Timestamp res_time;
    180     struct GNUNET_TIME_Timestamp replay_time;
    181 
    182     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    183             ANASTASIS_DB_do_update_account_lifetime (
    184               &accountPubP,
    185               &paymentSecretP,
    186               now,
    187               rel_time,
    188               &res_time));
    189     /* Lifetime is MAX(what the account had, now) + rel_time; the account
    190        already carries the transient lifetime insert_recdoc_payment() gave
    191        it, so all we can pin down is the lower bound. */
    192     FAILIF (GNUNET_TIME_timestamp_cmp (
    193               res_time,
    194               <,
    195               GNUNET_TIME_absolute_to_timestamp (
    196                 GNUNET_TIME_absolute_add (now.abs_time,
    197                                           rel_time))));
    198     /* Replaying the same payment must not extend the lifetime again. */
    199     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    200             ANASTASIS_DB_do_update_account_lifetime (
    201               &accountPubP,
    202               &paymentSecretP,
    203               now,
    204               rel_time,
    205               &replay_time));
    206     FAILIF (GNUNET_TIME_timestamp_cmp (replay_time,
    207                                        !=,
    208                                        res_time));
    209   }
    210   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    211           ANASTASIS_DB_get_recdoc_payment (
    212             &accountPubP,
    213             &paymentSecretP,
    214             &paid,
    215             &valid_counter,
    216             &pi_creation_date));
    217   {
    218     /* The same identifier under a different account must not be visible. */
    219     struct ANASTASIS_CRYPTO_AccountPublicKeyP otherPubP;
    220 
    221     memset (&otherPubP, 7, sizeof (otherPubP));
    222     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    223             ANASTASIS_DB_get_recdoc_payment (
    224               &otherPubP,
    225               &paymentSecretP,
    226               &paid,
    227               &valid_counter,
    228               &pi_creation_date));
    229   }
    230   {
    231     struct TALER_Amount paid_amount;
    232     struct TALER_Amount got_amount;
    233 
    234     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    235             ANASTASIS_DB_get_challenge_payment (
    236               &paymentSecretP,
    237               &truth_uuid,
    238               &paid,
    239               &got_amount));
    240     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    241             ANASTASIS_DB_insert_challenge_payment (
    242               &truth_uuid,
    243               &paymentSecretP,
    244               &amount));
    245     /* The wallet settles in a currency of its choosing, which need not be
    246        the one the order was quoted in.  What comes back out has to be
    247        that one, or a refund would be issued in the wrong currency. */
    248     GNUNET_assert (GNUNET_OK ==
    249                    TALER_string_to_amount ("CHF:23",
    250                                            &paid_amount));
    251     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    252             ANASTASIS_DB_update_to_challenge_payment_paid (
    253               &truth_uuid,
    254               &paymentSecretP,
    255               &paid_amount));
    256     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    257             ANASTASIS_DB_get_challenge_payment (
    258               &paymentSecretP,
    259               &truth_uuid,
    260               &paid,
    261               &got_amount));
    262     FAILIF (! paid);
    263     FAILIF (GNUNET_OK !=
    264             TALER_amount_cmp_currency (&paid_amount,
    265                                        &got_amount));
    266     FAILIF (0 != TALER_amount_cmp (&paid_amount,
    267                                    &got_amount));
    268   }
    269   FAILIF (ANASTASIS_DB_STORE_STATUS_SUCCESS !=
    270           ANASTASIS_DB_do_insert_recovery_document (
    271             &accountPubP,
    272             &accountSig,
    273             &recoveryDataHash,
    274             recovery_data,
    275             strlen (recovery_data),
    276             "meta-data",
    277             strlen ("meta-data"),
    278             &paymentSecretP,
    279             &docVersion));
    280   {
    281     uint32_t vrs;
    282     struct GNUNET_TIME_Timestamp exp;
    283 
    284     FAILIF (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED !=
    285             ANASTASIS_DB_get_account (
    286               &accountPubP,
    287               &exp,
    288               &r,
    289               &vrs));
    290   }
    291   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    292           ANASTASIS_DB_get_truth_key_share (
    293             &truth_uuid,
    294             &res_key_share));
    295   FAILIF (0 !=
    296           GNUNET_memcmp (&res_key_share,
    297                          &key_share));
    298 
    299   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    300           ANASTASIS_DB_get_recovery_document (
    301             &accountPubP,
    302             recversion,
    303             &res_account_sig,
    304             &res_recovery_data_hash,
    305             &recoverydatasize,
    306             &res_recovery_data));
    307   FAILIF (0 != memcmp (res_recovery_data,
    308                        recovery_data,
    309                        strlen (recovery_data)));
    310   GNUNET_free (res_recovery_data);
    311 
    312   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    313           ANASTASIS_DB_get_latest_recovery_document (
    314             &accountPubP,
    315             &res_account_sig,
    316             &res_recovery_data_hash,
    317             &recoverydatasize,
    318             &res_recovery_data,
    319             &res_version));
    320   FAILIF (0 != memcmp (res_recovery_data,
    321                        recovery_data,
    322                        strlen (recovery_data)));
    323   GNUNET_free (res_recovery_data);
    324 
    325   {
    326     struct GNUNET_TIME_Timestamp rt;
    327 
    328     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    329             ANASTASIS_DB_insert_challenge_code (
    330               &truth_uuid,
    331               GNUNET_TIME_UNIT_HOURS,
    332               GNUNET_TIME_UNIT_DAYS,
    333               3,                              /* retry counter */
    334               &rt,
    335               &challenge_code));
    336     FAILIF (! GNUNET_TIME_absolute_is_zero (rt.abs_time));
    337   }
    338   {
    339     struct GNUNET_TIME_Timestamp rt;
    340     uint64_t c2;
    341 
    342     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    343             ANASTASIS_DB_insert_challenge_code (
    344               &truth_uuid,
    345               GNUNET_TIME_UNIT_HOURS,
    346               GNUNET_TIME_UNIT_DAYS,
    347               3,                              /* retry counter */
    348               &rt,
    349               &c2));
    350     FAILIF (c2 != challenge_code);
    351   }
    352   ANASTASIS_hash_answer (123,
    353                          &c_hash);
    354   {
    355     bool sat;
    356     uint64_t r_code;
    357 
    358     FAILIF (ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH !=
    359             ANASTASIS_DB_do_verify_challenge_code (
    360               &truth_uuid,
    361               &c_hash,
    362               &r_code,
    363               &sat));
    364 
    365     ANASTASIS_hash_answer (challenge_code,
    366                            &c_hash);
    367     FAILIF (ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED !=
    368             ANASTASIS_DB_do_verify_challenge_code (
    369               &truth_uuid,
    370               &c_hash,
    371               &r_code,
    372               &sat));
    373 
    374     /* Burn the two remaining attempts of the (only) answerable code; the
    375        correct answer must then be rejected as well, until a new code is
    376        issued. */
    377     ANASTASIS_hash_answer (challenge_code + 1,
    378                            &c_hash);
    379     for (unsigned int i = 0; i<2; i++)
    380       FAILIF (ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH !=
    381               ANASTASIS_DB_do_verify_challenge_code (
    382                 &truth_uuid,
    383                 &c_hash,
    384                 &r_code,
    385                 &sat));
    386     FAILIF (ANASTASIS_DB_CODE_STATUS_RATE_LIMITED !=
    387             ANASTASIS_DB_do_verify_challenge_code (
    388               &truth_uuid,
    389               &c_hash,
    390               &r_code,
    391               &sat));
    392     ANASTASIS_hash_answer (challenge_code,
    393                            &c_hash);
    394     FAILIF (ANASTASIS_DB_CODE_STATUS_RATE_LIMITED !=
    395             ANASTASIS_DB_do_verify_challenge_code (
    396               &truth_uuid,
    397               &c_hash,
    398               &r_code,
    399               &sat));
    400   }
    401   if (-1 == result)
    402     result = 0;
    403 
    404 drop:
    405   GNUNET_break (GNUNET_OK ==
    406                 ANASTASIS_DB_drop_tables ());
    407   ANASTASIS_DB_fini ();
    408 }
    409 
    410 
    411 int
    412 main (int argc,
    413       char *const argv[])
    414 {
    415   const char *plugin_name;
    416   char *config_filename;
    417   char *testname;
    418   struct GNUNET_CONFIGURATION_Handle *cfg;
    419 
    420   result = -1;
    421   if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
    422   {
    423     GNUNET_break (0);
    424     return -1;
    425   }
    426   GNUNET_log_setup (argv[0], "DEBUG", NULL);
    427   plugin_name++;
    428   GNUNET_asprintf (&testname,
    429                    "%s",
    430                    plugin_name);
    431   GNUNET_asprintf (&config_filename,
    432                    "test_anastasis_db_%s.conf",
    433                    testname);
    434   cfg = GNUNET_CONFIGURATION_create (ANASTASIS_project_data ());
    435   if (GNUNET_OK !=
    436       GNUNET_CONFIGURATION_load (cfg,
    437                                  config_filename))
    438   {
    439     GNUNET_break (0);
    440     GNUNET_free (config_filename);
    441     GNUNET_free (testname);
    442     return 2;
    443   }
    444   GNUNET_SCHEDULER_run (&run,
    445                         cfg);
    446   GNUNET_CONFIGURATION_destroy (cfg);
    447   GNUNET_free (config_filename);
    448   GNUNET_free (testname);
    449   return result;
    450 }
    451 
    452 
    453 /* end of test_anastasis_db.c */