anastasis

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

anastasis-db_verify_challenge_code.c (6525B)


      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_verify_challenge_code.c
     18  * @brief Anastasis database: verify challenge code
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "anastasis-db_pg.h"
     23 #include "anastasis/anastasis-database/verify_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 struct CheckValidityContext
     30 {
     31   /**
     32    * Code to check for.
     33    */
     34   const struct GNUNET_HashCode *hashed_code;
     35 
     36   /**
     37    * Truth we are processing.
     38    */
     39   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid;
     40 
     41   /**
     42    * Set to the matching challenge code (if @e valid).
     43    */
     44   uint64_t code;
     45 
     46   /**
     47    * Set to true if a code matching @e hashed_code was found.
     48    */
     49   bool valid;
     50 
     51   /**
     52    * Set to true if a code matching @e hashed_code was set to 'satisfied' by the plugin.
     53    */
     54   bool satisfied;
     55 
     56   /**
     57    * Set to true if we had a database failure.
     58    */
     59   bool db_failure;
     60 
     61 };
     62 
     63 
     64 /**
     65  * Helper function for #postgres_verify_challenge_code().
     66  * To be called with the results of a SELECT statement
     67  * that has returned @a num_results results.
     68  *
     69  * @param cls closure of type `struct CheckValidityContext *`
     70  * @param result the postgres result
     71  * @param num_results the number of results in @a result
     72  */
     73 static void
     74 check_valid_code (void *cls,
     75                   PGresult *result,
     76                   unsigned int num_results)
     77 {
     78   struct CheckValidityContext *cvc = cls;
     79 
     80   for (unsigned int i = 0; i < num_results; i++)
     81   {
     82     uint64_t server_code;
     83     uint8_t sat;
     84     struct GNUNET_PQ_ResultSpec rs[] = {
     85       GNUNET_PQ_result_spec_uint64 ("code",
     86                                     &server_code),
     87       GNUNET_PQ_result_spec_auto_from_type ("satisfied",
     88                                             &sat),
     89       GNUNET_PQ_result_spec_end
     90     };
     91 
     92     if (GNUNET_OK !=
     93         GNUNET_PQ_extract_result (result,
     94                                   rs,
     95                                   i))
     96     {
     97       GNUNET_break (0);
     98       cvc->db_failure = true;
     99       return;
    100     }
    101     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    102                 "Found issued challenge %llu (client: %s)\n",
    103                 (unsigned long long) server_code,
    104                 GNUNET_h2s (cvc->hashed_code));
    105     {
    106       struct GNUNET_HashCode shashed_code;
    107 
    108       ANASTASIS_hash_answer (server_code,
    109                              &shashed_code);
    110       if (0 ==
    111           GNUNET_memcmp (&shashed_code,
    112                          cvc->hashed_code))
    113       {
    114         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    115                     "Challenge is valid challenge (%s)\n",
    116                     (0 != sat) ? "satisfied" : "not satisfied");
    117         cvc->valid = true;
    118         cvc->code = server_code;
    119         cvc->satisfied = (0 != sat);
    120       }
    121       else
    122       {
    123         /* count failures to prevent brute-force attacks */
    124         struct GNUNET_PQ_QueryParam params[] = {
    125           GNUNET_PQ_query_param_auto_from_type (cvc->truth_uuid),
    126           GNUNET_PQ_query_param_uint64 (&server_code),
    127           GNUNET_PQ_query_param_end
    128         };
    129         enum GNUNET_DB_QueryStatus qs;
    130 
    131         qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    132                                                  "challengecode_update_retry",
    133                                                  params);
    134         if (qs <= 0)
    135         {
    136           GNUNET_break (0);
    137           cvc->db_failure = true;
    138         }
    139       }
    140     }
    141   }
    142 }
    143 
    144 
    145 /**
    146  * Verify the provided code with the code on the server.
    147  * If the code matches the function will return with success, if the code
    148  * does not match, the retry counter will be decreased by one.
    149  *
    150  * @param cls closure
    151  * @param truth_uuid identification of the challenge which the code corresponds to
    152  * @param hashed_code code which the user provided and wants to verify
    153  * @param[out] code set to the original numeric code
    154  * @param[out] satisfied set to true if the challenge is set to satisfied
    155  * @return code validity status
    156  */
    157 enum ANASTASIS_DB_CodeStatus
    158 ANASTASIS_DB_verify_challenge_code (
    159   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    160   const struct GNUNET_HashCode *hashed_code,
    161   uint64_t *code,
    162   bool *satisfied)
    163 {
    164   struct CheckValidityContext cvc = {
    165     .truth_uuid = truth_uuid,
    166     .hashed_code = hashed_code,
    167   };
    168   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    169   struct GNUNET_PQ_QueryParam params[] = {
    170     GNUNET_PQ_query_param_auto_from_type (truth_uuid),
    171     GNUNET_PQ_query_param_timestamp (&now),
    172     GNUNET_PQ_query_param_end
    173   };
    174   enum GNUNET_DB_QueryStatus qs;
    175 
    176   *satisfied = false;
    177   PREPARE ("challengecode_update_retry",
    178            "UPDATE anastasis_challengecode"
    179            " SET retry_counter=retry_counter - 1"
    180            " WHERE truth_uuid=$1"
    181            "   AND code=$2"
    182            "   AND retry_counter != 0;");
    183   PREPARE ("challengecode_select",
    184            "SELECT "
    185            " code"
    186            ",satisfied"
    187            " FROM anastasis_challengecode"
    188            " WHERE truth_uuid=$1"
    189            "   AND expiration_date > $2"
    190            "   AND retry_counter != 0;");
    191   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    192                                              "challengecode_select",
    193                                              params,
    194                                              &check_valid_code,
    195                                              &cvc);
    196   if ( (qs < 0) ||
    197        (cvc.db_failure) )
    198     return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
    199   *code = cvc.code;
    200   if (cvc.valid)
    201   {
    202     *satisfied = cvc.satisfied;
    203     return ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED;
    204   }
    205   if (0 == qs)
    206     return ANASTASIS_DB_CODE_STATUS_NO_RESULTS;
    207   return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH;
    208 }
    209 
    210 
    211 /* end of anastasis-db_verify_challenge_code.c */