anastasis

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

anastasis-db_create_challenge_code.c (7199B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020-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.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file stasis/anastasis-db_create_challenge_code.c
     18  * @brief Anastasis database: create challenge code
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "anastasis-db_pg.h"
     23 #include "anastasis/anastasis-database/create_challenge_code.h"
     24 #include "anastasis/anastasis-database/transaction.h"
     25 #include "anastasis/anastasis-database/preflight.h"
     26 #include <taler/taler_pq_lib.h>
     27 
     28 
     29 /**
     30  * Create a new challenge code for a given challenge identified by the challenge
     31  * public key. The function will first check if there is already a valid code
     32  * for this challenge present and won't insert a new one in this case.
     33  *
     34  * @param truth_uuid the identifier for the challenge
     35  * @param rotation_period for how long is the code available
     36  * @param validity_period for how long is the code available
     37  * @param retry_counter amount of retries allowed
     38  * @param[out] retransmission_date when to next retransmit
     39  * @param[out] code set to the code which will be checked for later
     40  * @return transaction status,
     41  *        #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries,
     42  *        #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB
     43  */
     44 enum GNUNET_DB_QueryStatus
     45 ANASTASIS_DB_create_challenge_code (
     46   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
     47   struct GNUNET_TIME_Relative rotation_period,
     48   struct GNUNET_TIME_Relative validity_period,
     49   uint32_t retry_counter,
     50   struct GNUNET_TIME_Timestamp *retransmission_date,
     51   uint64_t *code)
     52 {
     53   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
     54   struct GNUNET_TIME_Timestamp expiration_date;
     55   struct GNUNET_TIME_Absolute ex_rot;
     56 
     57   PREPARE ("challengecode_select_meta",
     58            "SELECT "
     59            " code"
     60            ",retry_counter"
     61            ",retransmission_date"
     62            " FROM anastasis_challengecode"
     63            " WHERE truth_uuid=$1"
     64            "   AND expiration_date > $2"
     65            "   AND creation_date > $3"
     66            " ORDER BY creation_date DESC"
     67            " LIMIT 1;");
     68   PREPARE ("challengecode_insert",
     69            "INSERT INTO anastasis_challengecode "
     70            "(truth_uuid"
     71            ",code"
     72            ",creation_date"
     73            ",expiration_date"
     74            ",retry_counter"
     75            ") VALUES "
     76            "($1, $2, $3, $4, $5);");
     77   expiration_date = GNUNET_TIME_relative_to_timestamp (validity_period);
     78   ex_rot = GNUNET_TIME_absolute_subtract (now.abs_time,
     79                                           rotation_period);
     80   for (unsigned int retries = 0; retries<MAX_RETRIES; retries++)
     81   {
     82     if (GNUNET_OK !=
     83         ANASTASIS_DB_start (
     84           "create_challenge_code"))
     85     {
     86       GNUNET_break (0);
     87       return GNUNET_DB_STATUS_HARD_ERROR;
     88     }
     89 
     90     {
     91       uint32_t old_retry_counter;
     92       struct GNUNET_PQ_QueryParam params[] = {
     93         GNUNET_PQ_query_param_auto_from_type (truth_uuid),
     94         GNUNET_PQ_query_param_timestamp (&now),
     95         GNUNET_PQ_query_param_absolute_time (&ex_rot),
     96         GNUNET_PQ_query_param_end
     97       };
     98       struct GNUNET_PQ_ResultSpec rs[] = {
     99         GNUNET_PQ_result_spec_uint64 ("code",
    100                                       code),
    101         GNUNET_PQ_result_spec_uint32 ("retry_counter",
    102                                       &old_retry_counter),
    103         GNUNET_PQ_result_spec_timestamp ("retransmission_date",
    104                                          retransmission_date),
    105         GNUNET_PQ_result_spec_end
    106       };
    107       enum GNUNET_DB_QueryStatus qs;
    108 
    109       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    110                                                      "challengecode_select_meta",
    111                                                      params,
    112                                                      rs);
    113       switch (qs)
    114       {
    115       case GNUNET_DB_STATUS_HARD_ERROR:
    116         GNUNET_break (0);
    117         ANASTASIS_DB_rollback ();
    118         return qs;
    119       case GNUNET_DB_STATUS_SOFT_ERROR:
    120         goto retry;
    121       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    122         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    123                     "No active challenge found, creating a fresh one\n");
    124         break;
    125       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    126         if (0 == old_retry_counter)
    127         {
    128           ANASTASIS_DB_rollback ();
    129           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    130                       "Active challenge %llu has zero tries left, refusing to create another one\n",
    131                       (unsigned long long) *code);
    132           return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    133         }
    134         ANASTASIS_DB_rollback ();
    135         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    136                     "Active challenge has %u tries left, returning old challenge %llu\n",
    137                     (unsigned int) old_retry_counter,
    138                     (unsigned long long) *code);
    139         return qs;
    140       }
    141     }
    142 
    143     *code = GNUNET_CRYPTO_random_u64 (ANASTASIS_PIN_MAX_VALUE);
    144     *retransmission_date = GNUNET_TIME_UNIT_ZERO_TS;
    145     {
    146       struct GNUNET_PQ_QueryParam params[] = {
    147         GNUNET_PQ_query_param_auto_from_type (truth_uuid),
    148         GNUNET_PQ_query_param_uint64 (code),
    149         GNUNET_PQ_query_param_timestamp (&now),
    150         GNUNET_PQ_query_param_timestamp (&expiration_date),
    151         GNUNET_PQ_query_param_uint32 (&retry_counter),
    152         GNUNET_PQ_query_param_end
    153       };
    154       enum GNUNET_DB_QueryStatus qs;
    155 
    156       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    157                                                "challengecode_insert",
    158                                                params);
    159       switch (qs)
    160       {
    161       case GNUNET_DB_STATUS_HARD_ERROR:
    162         ANASTASIS_DB_rollback ();
    163         return GNUNET_DB_STATUS_HARD_ERROR;
    164       case GNUNET_DB_STATUS_SOFT_ERROR:
    165         goto retry;
    166       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    167         GNUNET_break (0);
    168         ANASTASIS_DB_rollback ();
    169         return GNUNET_DB_STATUS_HARD_ERROR;
    170       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    171         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    172                     "Created fresh challenge with %u tries left\n",
    173                     (unsigned int) retry_counter);
    174         break;
    175       }
    176     }
    177 
    178     {
    179       enum GNUNET_DB_QueryStatus qs;
    180 
    181       qs = ANASTASIS_DB_commit ();
    182       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    183         goto retry;
    184       if (qs < 0)
    185         return qs;
    186     }
    187     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    188 retry:
    189     ANASTASIS_DB_rollback ();
    190   }
    191   return GNUNET_DB_STATUS_SOFT_ERROR;
    192 }
    193 
    194 
    195 /* end of anastasis-db_create_challenge_code.c */