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