gnunet-svn
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]