[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-anastasis] branch master updated: update statis API and service t
From: |
gnunet |
Subject: |
[taler-anastasis] branch master updated: update statis API and service to properly generate, validate, rotate and expire challenges |
Date: |
Mon, 08 Feb 2021 19:20:21 +0100 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository anastasis.
The following commit(s) were added to refs/heads/master by this push:
new 94ab8f9 update statis API and service to properly generate, validate,
rotate and expire challenges
94ab8f9 is described below
commit 94ab8f9e94cb4855a7f6704b0c929cc3af7491dd
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Feb 8 19:20:18 2021 +0100
update statis API and service to properly generate, validate, rotate and
expire challenges
---
src/backend/anastasis-httpd_truth.c | 61 +++--
src/include/anastasis_database_plugin.h | 41 +++-
src/stasis/plugin_anastasis_postgres.c | 403 ++++++++++++++++++++------------
src/stasis/stasis-0001.sql | 7 +-
src/stasis/test_anastasis_db.c | 45 ++--
5 files changed, 355 insertions(+), 202 deletions(-)
diff --git a/src/backend/anastasis-httpd_truth.c
b/src/backend/anastasis-httpd_truth.c
index 5bb73b4..2149605 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -38,6 +38,11 @@
#define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \
GNUNET_TIME_UNIT_MINUTES, 30)
+/**
+ * How many retries do we allow per code?
+ */
+#define INITIAL_RETRY_COUNTER 3
+
struct GetContext
{
@@ -807,6 +812,11 @@ AH_handler_truth_get (struct MHD_Connection *connection,
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
NULL);
}
+ // FIXME: do something here to rate-limit
+ // brute force attempts (by checking against the timestamp
+ // from 'mark_challenge_sent' and refusing if the response
+ // is provided too quickly again!
+
if ( (decrypted_truth_size != sizeof (challenge_response)) ||
(0 != memcmp (&challenge_response,
decrypted_truth,
@@ -814,7 +824,10 @@ AH_handler_truth_get (struct MHD_Connection *connection,
{
GNUNET_break_op (0);
GNUNET_free (decrypted_truth);
- // FIXME: mark in DB that we did it (now, for
code_retransmission_frequency!)
+ qs = db->mark_challenge_sent (db->cls,
+ &gc->truth_public_key,
+ code);
+ GNUNET_break (0 < qs);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
@@ -887,43 +900,43 @@ AH_handler_truth_get (struct MHD_Connection *connection,
/* Setup challenge and begin authorization process */
{
+ struct GNUNET_TIME_Absolute transmission_date;
uint64_t code;
enum GNUNET_DB_QueryStatus qs;
- code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
- 999999);
- // FIXME: this is more complex:
- // - p->code_rotation_period
- // - p->code_validity_period
- // - p->code_retransmission_frequency
- // all MUST be considered here!
- // -> DB API change needed!
- qs = db->store_challenge_code (db->cls,
- &gc->truth_public_key,
- code,
- p->code_validity_period,
- 3);
+ qs = db->create_challenge_code (db->cls,
+ &gc->truth_public_key,
+ p->code_rotation_period,
+ p->code_validity_period,
+ INITIAL_RETRY_COUNTER,
+ &transmission_date,
+ &code);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
case GNUNET_DB_STATUS_SOFT_ERROR:
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_break (0);
GNUNET_free (decrypted_truth);
return TALER_MHD_reply_with_error (gc->connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"store_challenge_code");
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* challenge code was stored successfully*/
+ break;
+ }
+
+ if (GNUNET_TIME_absolute_get_duration (transmission_date).rel_value_us <
+ p->code_retransmission_frequency.rel_value_us)
+ {
+ /* Too early for a retransmission! */
GNUNET_free (decrypted_truth);
return TALER_MHD_reply_with_error (gc->connection,
MHD_HTTP_ALREADY_REPORTED,
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_ACTIVE,
NULL);
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* challenge code was stored successfully*/
- break;
}
-
gc->as = gc->authorization->start (gc->authorization->cls,
&AH_trigger_daemon,
NULL,
@@ -953,7 +966,10 @@ AH_handler_truth_get (struct MHD_Connection *connection,
{
case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
/* all good, challenge sent! */
- // FIXME: mark in DB that we did it (now, for
code_retransmission_frequency!)
+ qs = db->mark_challenge_sent (db->cls,
+ &gc->truth_public_key,
+ code);
+ GNUNET_break (0 < qs);
break;
case ANASTASIS_AUTHORIZATION_RES_FAILED:
/* sending challenge failed */
@@ -964,7 +980,10 @@ AH_handler_truth_get (struct MHD_Connection *connection,
return MHD_YES;
case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
/* Challenge sent successfully, but HTTP reply failed */
- // FIXME: mark in DB that we did it (now, for
code_retransmission_frequency!)
+ qs = db->mark_challenge_sent (db->cls,
+ &gc->truth_public_key,
+ code);
+ GNUNET_break (0 < qs);
gc->authorization->cleanup (gc->as);
return MHD_NO;
case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
diff --git a/src/include/anastasis_database_plugin.h
b/src/include/anastasis_database_plugin.h
index 43f407f..5c5b412 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -463,6 +463,7 @@ struct ANASTASIS_DatabasePlugin
const struct ANASTASIS_PaymentSecretP *payment_secret,
bool *paid);
+
/**
* Increment account lifetime.
*
@@ -512,10 +513,10 @@ struct ANASTASIS_DatabasePlugin
* @return transaction status
*/
enum ANASTASIS_DB_CodeStatus
- (*verify_challenge_code)(void *cls,
- const struct
- ANASTASIS_CRYPTO_TruthPublicKeyP *truth_pub,
- const struct GNUNET_HashCode *hashed_code);
+ (*verify_challenge_code)(
+ void *cls,
+ const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_pub,
+ const struct GNUNET_HashCode *hashed_code);
/**
* Insert a new challenge code for a given challenge identified by the
challenge
@@ -524,20 +525,38 @@ struct ANASTASIS_DatabasePlugin
*
* @param cls closure
* @param truth_public_key the identifier for the challenge
- * @param code code which will be safed to check later
- * @param expiration_time for how long is the code available
+ * @param rotation_period for how long is the code available
+ * @param validity_period for how long is the code available
* @param retry_counter amount of retries allowed
+ * @param[out] retransmission_date when to next retransmit
+ * @param[out] code set to the code which will be checked for later
* @return transaction status,
- * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if conflicting challenge
exists in DB,
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a code was already in the
DB
* #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if this @a code is now in the
DB
*/
enum GNUNET_DB_QueryStatus
- (*store_challenge_code)(
+ (*create_challenge_code)(
+ void *cls,
+ const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
+ struct GNUNET_TIME_Relative rotation_period,
+ struct GNUNET_TIME_Relative validity_period,
+ unsigned int retry_counter,
+ struct GNUNET_TIME_Absolute *retransmission_date,
+ uint64_t *code);
+
+
+ /**
+ * Remember in the database that we successfully sent a challenge.
+ *
+ * @param cls closure
+ * @param truth_public_key the identifier for the challenge
+ * @param code the challenge that was sent
+ */
+ enum GNUNET_DB_QueryStatus
+ (*mark_challenge_sent)(
void *cls,
const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
- uint64_t code,
- struct GNUNET_TIME_Relative expiration_time,
- unsigned int retry_counter);
+ uint64_t code);
/**
diff --git a/src/stasis/plugin_anastasis_postgres.c
b/src/stasis/plugin_anastasis_postgres.c
index c2727b7..2dd6a6a 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -1,6 +1,6 @@
/*
This file is part of Anastasis
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020, 2021 Taler Systems SA
Anastasis is free software; you can redistribute it and/or modify it under
the
terms of the GNU Lesser General Public License as published by the Free
Software
@@ -259,68 +259,6 @@ postgres_gc (void *cls,
}
-/**
- * Check if there is already a valid code.
- *
- * @param pg postgres closure
- * @param truth_public_key identifier for a challenge
- * @param code code which will be safed to check later
- * @param creation_date timestamp
- * @return transaction status, NULL if codes are different
- */
-static enum ANASTASIS_DB_CodeStatus
-check_valid_code (
- struct PostgresClosure *pg,
- const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
- const struct GNUNET_HashCode *hashed_code,
- struct GNUNET_TIME_Absolute creation_date)
-{
- uint64_t server_code;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (truth_public_key),
- TALER_PQ_query_param_absolute_time (&creation_date),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("code",
- &server_code),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "challengecode_select",
- params,
- rs);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return ANASTASIS_DB_CODE_STATUS_SOFT_ERROR;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- return ANASTASIS_DB_CODE_STATUS_NO_RESULTS;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- {
- struct GNUNET_HashCode shashed_code;
-
- ANASTASIS_hash_answer (server_code,
- &shashed_code);
- if (0 ==
- GNUNET_memcmp (&shashed_code,
- hashed_code))
- {
- return ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED;
- }
- return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH;
- }
- default:
- GNUNET_break (0);
- return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- }
-}
-
-
/**
* Closure for #payment_by_account_cb.
*/
@@ -1332,6 +1270,109 @@ postgres_get_recovery_document (
}
+/**
+ * Closure for check_valid_code().
+ */
+struct CheckValidityContext
+{
+ /**
+ * Code to check for.
+ */
+ const struct GNUNET_HashCode *hashed_code;
+
+ /**
+ * Truth we are processing.
+ */
+ const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_pub;
+
+ /**
+ * Database context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to true if a code matching @e hashed_code was found.
+ */
+ bool valid;
+
+ /**
+ * Set to true if we had a database failure.
+ */
+ bool db_failure;
+
+};
+
+
+/**
+ * Helper function for #postgres_verify_challenge_code().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CheckValidityContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+check_valid_code (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct CheckValidityContext *cvc = cls;
+ struct PostgresClosure *pg = cvc->pg;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ uint64_t server_code;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("code",
+ &server_code),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ cvc->db_failure = true;
+ return;
+ }
+ {
+ struct GNUNET_HashCode shashed_code;
+
+ ANASTASIS_hash_answer (server_code,
+ &shashed_code);
+ if (0 ==
+ GNUNET_memcmp (&shashed_code,
+ cvc->hashed_code))
+ {
+ cvc->valid = true;
+ }
+ else
+ {
+ /* count failures to prevent brute-force attacks */
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (cvc->truth_pub),
+ GNUNET_PQ_query_param_uint64 (&server_code),
+ GNUNET_PQ_query_param_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "challengecode_update_retry",
+ params);
+ if (qs <= 0)
+ {
+ GNUNET_break (0);
+ cvc->db_failure = true;
+ }
+ }
+ }
+ }
+}
+
+
/**
* Verify the provided code with the code on the server.
* If the code matches the function will return with success, if the code
@@ -1349,49 +1390,34 @@ postgres_verify_challenge_code (
const struct GNUNET_HashCode *hashed_code)
{
struct PostgresClosure *pg = cls;
+ struct CheckValidityContext cvc = {
+ .truth_pub = truth_pub,
+ .hashed_code = hashed_code,
+ .pg = pg
+ };
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (truth_pub),
+ TALER_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Absolute time_now = GNUNET_TIME_absolute_get ();
- enum ANASTASIS_DB_CodeStatus cs;
check_connection (pg);
- GNUNET_TIME_round_abs (&time_now);
- /* Check if there is already a valid code */
- cs = check_valid_code (pg,
- truth_pub,
- hashed_code,
- time_now);
- if (cs != ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH)
- {
- if (ANASTASIS_DB_CODE_STATUS_SOFT_ERROR == cs)
- {
- GNUNET_break (0);
- return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- }
- return cs;
- }
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "challengecode_update_retry",
- params);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_break (0);
- return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH;
- default:
- GNUNET_break (0);
+ GNUNET_TIME_round_abs (&now);
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "challengecode_select",
+ params,
+ &check_valid_code,
+ &cvc);
+ if ( (qs < 0) ||
+ (cvc.db_failure) )
return ANASTASIS_DB_CODE_STATUS_HARD_ERROR;
- }
+ if (cvc.valid)
+ return ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED;
+ if (0 == qs)
+ return ANASTASIS_DB_CODE_STATUS_NO_RESULTS;
+ return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH;
}
@@ -1463,77 +1489,95 @@ postgres_update_challenge_payment (
/**
- * Insert a new challenge code for a given challenge identified by the
challenge
+ * Create a new challenge code for a given challenge identified by the
challenge
* public key. The function will first check if there is already a valid code
* for this challenge present and won't insert a new one in this case.
*
* @param cls closure
* @param truth_public_key the identifier for the challenge
- * @param code code which will be safed to check later
- * @param expiration_time for how long is the code available
+ * @param rotation_period for how long is the code available
+ * @param validity_period for how long is the code available
* @param retry_counter amount of retries allowed
+ * @param[out] retransmission_date when to next retransmit
+ * @param[out] code set to the code which will be checked for later
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
-postgres_store_challenge_code (
+postgres_create_challenge_code (
void *cls,
const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
- uint64_t code,
- struct GNUNET_TIME_Relative expiration_time,
- unsigned int retry_counter)
+ struct GNUNET_TIME_Relative rotation_period,
+ struct GNUNET_TIME_Relative validity_period,
+ unsigned int retry_counter,
+ struct GNUNET_TIME_Absolute *retransmission_date,
+ uint64_t *code)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Absolute creation_date = GNUNET_TIME_absolute_get ();
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
struct GNUNET_TIME_Absolute expiration_date;
- struct GNUNET_HashCode hashed_code;
- enum ANASTASIS_DB_CodeStatus cs;
+ struct GNUNET_TIME_Absolute ex_rot;
check_connection (pg);
- GNUNET_TIME_round_abs (&creation_date);
- expiration_date = GNUNET_TIME_absolute_add (creation_date,
- expiration_time);
-
- /* Check if there is already a valid code */
- ANASTASIS_hash_answer (code,
- &hashed_code);
+ GNUNET_TIME_round_abs (&now);
+ expiration_date = GNUNET_TIME_absolute_add (now,
+ validity_period);
+ ex_rot = GNUNET_TIME_absolute_subtract (now,
+ rotation_period);
for (unsigned int retries = 0; retries<MAX_RETRIES; retries++)
{
if (GNUNET_OK !=
begin_transaction (pg,
- "store_challenge_code"))
+ "create_challenge_code"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- cs = check_valid_code (pg,
- truth_public_key,
- &hashed_code,
- creation_date);
- switch (cs)
+
{
- case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
- /* conflicting challenge is in DB, refuse to change! */
- rollback (pg);
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
- rollback (pg);
- return GNUNET_DB_STATUS_HARD_ERROR;
- case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
- goto retry;
- case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
- break; /* continue below */
- case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
- /* same value already stored, claim success */
- rollback (pg);
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (truth_public_key),
+ TALER_PQ_query_param_absolute_time (&now),
+ TALER_PQ_query_param_absolute_time (&ex_rot),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("code",
+ code),
+ GNUNET_PQ_result_spec_absolute_time ("retransmission_date",
+ retransmission_date),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+
"challengecode_select_meta",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ goto retry;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ rollback (pg);
+ return qs;
+ }
}
+ *code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ 999999);
+ *retransmission_date = GNUNET_TIME_UNIT_ZERO_ABS;
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (truth_public_key),
- GNUNET_PQ_query_param_uint64 (&code),
- TALER_PQ_query_param_absolute_time (&creation_date),
+ GNUNET_PQ_query_param_uint64 (code),
+ TALER_PQ_query_param_absolute_time (&now),
TALER_PQ_query_param_absolute_time (&expiration_date),
GNUNET_PQ_query_param_uint32 (&retry_counter),
GNUNET_PQ_query_param_end
@@ -1550,8 +1594,9 @@ postgres_store_challenge_code (
case GNUNET_DB_STATUS_SOFT_ERROR:
goto retry;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0);
rollback (pg);
- return GNUNET_DB_STATUS_SOFT_ERROR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
}
@@ -1569,6 +1614,37 @@ retry:
}
+/**
+ * Remember in the database that we successfully sent a challenge.
+ *
+ * @param cls closure
+ * @param truth_public_key the identifier for the challenge
+ * @param code the challenge that was sent
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_mark_challenge_sent (
+ void *cls,
+ const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
+ uint64_t code)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (truth_public_key),
+ GNUNET_PQ_query_param_uint64 (&code),
+ TALER_PQ_query_param_absolute_time (&now),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ now = GNUNET_TIME_absolute_get ();
+ GNUNET_TIME_round_abs (&now);
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "challengecode_mark_sent",
+ params);
+}
+
+
/**
* Function called to remove all expired codes from the database.
* FIXME maybe implemented as part of postgres_gc() in the future.
@@ -1838,20 +1914,44 @@ libanastasis_plugin_db_postgres_init (void *cls)
5),
GNUNET_PQ_make_prepare ("challengecode_select",
"SELECT "
- "code "
- "FROM "
- "anastasis_challengecode "
- "WHERE truth_public_key =$1 "
- "AND expiration_date > $2 "
- "AND retry_counter > 0;",
+ " code "
+ " FROM anastasis_challengecode"
+ " WHERE truth_public_key=$1"
+ " AND expiration_date > $2"
+ " AND retry_counter > 0;",
+ 2),
+ GNUNET_PQ_make_prepare ("challengecode_select_meta",
+ "SELECT "
+ " code"
+ ",retransmission_date"
+ " FROM anastasis_challengecode"
+ " WHERE truth_public_key=$1"
+ " AND expiration_date > $2"
+ " AND creation_date > $3"
+ " AND retry_counter > 0"
+ " ORDER BY creation_date DESC"
+ " LIMIT 1;",
2),
GNUNET_PQ_make_prepare ("challengecode_update_retry",
- "UPDATE anastasis_challengecode "
- "SET "
- "retry_counter = retry_counter -1 "
- "WHERE truth_public_key =$1 "
- "AND retry_counter > 0;",
+ "UPDATE anastasis_challengecode"
+ " SET retry_counter=retry_counter - 1"
+ " WHERE truth_public_key=$1"
+ " AND code=$2"
+ " AND retry_counter > 0;",
1),
+ GNUNET_PQ_make_prepare ("challengecode_mark_sent",
+ "UPDATE anastasis_challengecode"
+ " SET retransmission_date=$3"
+ " WHERE truth_public_key=$1"
+ " AND code=$2"
+ " AND creation_date IN"
+ " (SELECT creation_date"
+ " FROM anastasis_challengecode"
+ " WHERE truth_public_key=$1"
+ " AND code=$2"
+ " ORDER BY creation_date DESC"
+ " LIMIT 1);",
+ 3),
GNUNET_PQ_make_prepare ("gc_challengecodes",
"DELETE FROM anastasis_challengecode "
"WHERE "
@@ -1907,7 +2007,8 @@ libanastasis_plugin_db_postgres_init (void *cls)
plugin->start = &begin_transaction;
plugin->check_connection = &check_connection;
plugin->verify_challenge_code = &postgres_verify_challenge_code;
- plugin->store_challenge_code = &postgres_store_challenge_code;
+ plugin->create_challenge_code = &postgres_create_challenge_code;
+ plugin->mark_challenge_sent = &postgres_mark_challenge_sent;
plugin->challenge_gc = &postgres_challenge_gc;
plugin->record_challenge_payment = &postgres_record_challenge_payment;
plugin->check_challenge_payment = &postgres_check_challenge_payment;
diff --git a/src/stasis/stasis-0001.sql b/src/stasis/stasis-0001.sql
index 532936e..d0a5ef6 100644
--- a/src/stasis/stasis-0001.sql
+++ b/src/stasis/stasis-0001.sql
@@ -135,8 +135,9 @@ COMMENT ON COLUMN anastasis_recoverydocument.recovery_data
CREATE TABLE IF NOT EXISTS anastasis_challengecode
(truth_public_key BYTEA NOT NULL,
code INT8 NOT NULL,
- creation_date TIMESTAMP NOT NULL DEFAULT NOW(),
- expiration_date TIMESTAMP NOT NULL,
+ creation_date INT8 NOT NULL,
+ expiration_date INT8 NOT NULL,
+ retransmission_date INT8 NOT NULL DEFAULT 0,
retry_counter INT4 NOT NULL);
COMMENT ON TABLE anastasis_challengecode
IS 'Stores a code which is checked for the authentication by SMS, E-Mail..';
@@ -146,6 +147,8 @@ COMMENT ON COLUMN anastasis_challengecode.code
IS 'The pin code which is sent to the user and verified';
COMMENT ON COLUMN anastasis_challengecode.creation_date
IS 'Creation date of the code';
+COMMENT ON COLUMN anastasis_challengecode.retransmission_date
+ IS 'When did we last transmit the challenge to the user';
COMMENT ON COLUMN anastasis_challengecode.expiration_date
IS 'When will the code expire';
COMMENT ON COLUMN anastasis_challengecode.retry_counter
diff --git a/src/stasis/test_anastasis_db.c b/src/stasis/test_anastasis_db.c
index 7d7d6fa..7ab6bb6 100644
--- a/src/stasis/test_anastasis_db.c
+++ b/src/stasis/test_anastasis_db.c
@@ -64,7 +64,6 @@ run (void *cls)
struct GNUNET_HashCode recoveryDataHash;
struct GNUNET_HashCode res_recovery_data_hash;
struct GNUNET_HashCode r;
- struct GNUNET_TIME_Relative challenge_expiration;
struct GNUNET_TIME_Relative rel_time;
struct ANASTASIS_CRYPTO_TruthPublicKeyP truth_public_key;
struct ANASTASIS_CRYPTO_NonceP truth_nonce;
@@ -109,7 +108,6 @@ run (void *cls)
return;
}
- challenge_expiration = GNUNET_TIME_UNIT_HOURS;
GNUNET_CRYPTO_hash (recovery_data,
strlen (recovery_data),
&recoveryDataHash);
@@ -170,7 +168,6 @@ run (void *cls)
plugin->check_challenge_payment (plugin->cls,
&paymentSecretP,
&paid));
-
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->record_challenge_payment (plugin->cls,
&truth_public_key,
@@ -184,6 +181,7 @@ run (void *cls)
plugin->check_challenge_payment (plugin->cls,
&paymentSecretP,
&paid));
+ FAILIF (! paid);
FAILIF (ANASTASIS_DB_STORE_STATUS_SUCCESS !=
plugin->store_recovery_document (plugin->cls,
&accountPubP,
@@ -238,20 +236,33 @@ run (void *cls)
strlen (recovery_data)));
GNUNET_free (res_recovery_data);
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->store_challenge_code (plugin->cls,
- &truth_public_key,
- challenge_code,
- challenge_expiration,
- 3));
-
-
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->store_challenge_code (plugin->cls,
- &truth_public_key,
- challenge_code,
- challenge_expiration,
- 3));
+ {
+ struct GNUNET_TIME_Absolute rt;
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->create_challenge_code (plugin->cls,
+ &truth_public_key,
+ GNUNET_TIME_UNIT_HOURS,
+ GNUNET_TIME_UNIT_DAYS,
+ 3, /* retry counter */
+ &rt,
+ &challenge_code));
+ FAILIF (0 != rt.abs_value_us);
+ }
+ {
+ struct GNUNET_TIME_Absolute rt;
+ uint64_t c2;
+
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->create_challenge_code (plugin->cls,
+ &truth_public_key,
+ GNUNET_TIME_UNIT_HOURS,
+ GNUNET_TIME_UNIT_DAYS,
+ 3, /* retry counter */
+ &rt,
+ &c2));
+ FAILIF (c2 != challenge_code);
+ }
ANASTASIS_hash_answer (123,
&c_hash);
FAILIF (ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH !=
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-anastasis] branch master updated: update statis API and service to properly generate, validate, rotate and expire challenges,
gnunet <=