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