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 */