gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-anastasis] branch master updated: theoretically, this completes t


From: gnunet
Subject: [taler-anastasis] branch master updated: theoretically, this completes the TOTP implementation, alas completely untested
Date: Tue, 28 Sep 2021 15:50:31 +0200

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 435950e  theoretically, this completes the TOTP implementation, alas 
completely untested
435950e is described below

commit 435950ee10fc3d58f7ff992a2c2a2a3f73efa806
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Sep 28 15:50:28 2021 +0200

    theoretically, this completes the TOTP implementation, alas completely 
untested
---
 .../anastasis_authorization_plugin_totp.c          |  47 ++-
 src/backend/anastasis-httpd_truth.c                | 374 ++++++++++++++-------
 src/include/anastasis_database_plugin.h            |   2 +-
 src/reducer/anastasis_api_recovery_redux.c         |   9 +-
 4 files changed, 300 insertions(+), 132 deletions(-)

diff --git a/src/authorization/anastasis_authorization_plugin_totp.c 
b/src/authorization/anastasis_authorization_plugin_totp.c
index 6fcdd39..ee1ab3f 100644
--- a/src/authorization/anastasis_authorization_plugin_totp.c
+++ b/src/authorization/anastasis_authorization_plugin_totp.c
@@ -59,14 +59,14 @@ struct ANASTASIS_AUTHORIZATION_State
   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
 
   /**
-   * Our context.
+   * Was the challenge satisfied?
    */
-  const struct ANASTASIS_AuthorizationContext *ac;
+  struct GNUNET_HashCode valid_replies[TIME_INTERVAL_RANGE * 2 + 1];
 
   /**
-   * Was the challenge satisfied?
+   * Our context.
    */
-  bool ok;
+  const struct ANASTASIS_AuthorizationContext *ac;
 
 };
 
@@ -212,9 +212,9 @@ compute_totp (int time_off,
  * @param trigger_cls closure for @a trigger
  * @param truth_uuid Identifier of the challenge, to be (if possible) included 
in the
  *             interaction with the user
- * @param code set to secret code that the user provided to satisfy the 
challenge in
- *             the main anastasis protocol
- * @param data input to validate (i.e. the shared secret)
+ * @param code always 0 (direct validation, backend does
+ *             not generate a code in this mode)
+ * @param data truth for input to validate (i.e. the shared secret)
  * @param data_length number of bytes in @a data
  * @return state to track progress on the authorization operation, NULL on 
failure
  */
@@ -230,7 +230,9 @@ totp_start (void *cls,
   const struct ANASTASIS_AuthorizationContext *ac = cls;
   struct ANASTASIS_AUTHORIZATION_State *as;
   uint64_t want;
+  unsigned int off = 0;
 
+  GNUNET_assert (0 == code);
   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
   as->ac = ac;
   as->truth_uuid = *truth_uuid;
@@ -241,8 +243,8 @@ totp_start (void *cls,
     want = compute_totp (i,
                          data,
                          data_length);
-    if (code == want)
-      as->ok = true;
+    ANASTASIS_hash_answer (want,
+                           &as->valid_replies[off++]);
   }
   return as;
 }
@@ -264,9 +266,32 @@ totp_process (struct ANASTASIS_AUTHORIZATION_State *as,
   MHD_RESULT mres;
   const char *mime;
   const char *lang;
+  const char *challenge_response_s;
+  struct GNUNET_HashCode challenge_response;
+
+  challenge_response_s = MHD_lookup_connection_value (connection,
+                                                      MHD_GET_ARGUMENT_KIND,
+                                                      "response");
+  if ( (NULL == challenge_response_s) ||
+       (GNUNET_OK !=
+        GNUNET_CRYPTO_hash_from_string (challenge_response_s,
+                                        &challenge_response)) )
+  {
+    GNUNET_break_op (0);
+    mres = TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                       "response");
+    if (MHD_YES != mres)
+      return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+    return ANASTASIS_AUTHORIZATION_RES_FAILED;
 
-  if (as->ok)
-    return ANASTASIS_AUTHORIZATION_RES_FINISHED;
+  }
+  for (unsigned int i = 0; i<=TIME_INTERVAL_RANGE * 2; i++)
+    if (0 ==
+        GNUNET_memcmp (&challenge_response,
+                       &as->valid_replies[i]))
+      return ANASTASIS_AUTHORIZATION_RES_FINISHED;
   mime = MHD_lookup_connection_value (connection,
                                       MHD_HEADER_KIND,
                                       MHD_HTTP_HEADER_ACCEPT);
diff --git a/src/backend/anastasis-httpd_truth.c 
b/src/backend/anastasis-httpd_truth.c
index 4dd3ddc..aedd0a2 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -881,6 +881,38 @@ return_key_share (
 }
 
 
+/**
+ * Mark @a gc as suspended and update the respective
+ * data structures and jobs.
+ *
+ * @param[in,out] gc context of the suspended operation
+ */
+static void
+gc_suspended (struct GetContext *gc)
+{
+  gc->suspended = true;
+  if (NULL == AH_to_heap)
+    AH_to_heap = GNUNET_CONTAINER_heap_create (
+      GNUNET_CONTAINER_HEAP_ORDER_MIN);
+  gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
+                                         gc,
+                                         gc->timeout.abs_value_us);
+  if (NULL != to_task)
+  {
+    GNUNET_SCHEDULER_cancel (to_task);
+    to_task = NULL;
+  }
+  {
+    struct GetContext *rn;
+
+    rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
+    to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
+                                       &do_timeout,
+                                       NULL);
+  }
+}
+
+
 /**
  * Run the authorization method-specific 'process' function and continue
  * based on its result with generating an HTTP response.
@@ -923,26 +955,7 @@ run_authorization_process (struct MHD_Connection 
*connection,
     return MHD_YES;
   case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
     /* connection was suspended */
-    gc->suspended = true;
-    if (NULL == AH_to_heap)
-      AH_to_heap = GNUNET_CONTAINER_heap_create (
-        GNUNET_CONTAINER_HEAP_ORDER_MIN);
-    gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
-                                           gc,
-                                           gc->timeout.abs_value_us);
-    if (NULL != to_task)
-    {
-      GNUNET_SCHEDULER_cancel (to_task);
-      to_task = NULL;
-    }
-    {
-      struct GetContext *rn;
-
-      rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
-      to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
-                                         &do_timeout,
-                                         NULL);
-    }
+    gc_suspended (gc);
     return MHD_YES;
   case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
     /* Challenge sent successfully */
@@ -978,6 +991,212 @@ run_authorization_process (struct MHD_Connection 
*connection,
 }
 
 
+/**
+ * Use the database to rate-limit queries to the
+ * authentication procedure, but without actually
+ * storing 'real' challenge codes.
+ *
+ * @param[in,out] gc context to rate limit requests for
+ * @return #GNUNET_OK if rate-limiting passes,
+ *         #GNUNET_NO if a reply was sent (rate limited)
+ *         #GNUNET_SYSERR if we failed and no reply
+ *                        was queued
+ */
+static enum GNUNET_GenericReturnValue
+rate_limit (struct GetContext *gc)
+{
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_TIME_Absolute rt;
+  uint64_t code;
+  enum ANASTASIS_DB_CodeStatus cs;
+  struct GNUNET_HashCode hc;
+  bool satisfied;
+  uint64_t dummy;
+
+  rt = GNUNET_TIME_UNIT_FOREVER_ABS;
+  qs = db->create_challenge_code (db->cls,
+                                  &gc->truth_uuid,
+                                  MAX_QUESTION_FREQ,
+                                  GNUNET_TIME_UNIT_HOURS,
+                                  INITIAL_RETRY_COUNTER,
+                                  &rt,
+                                  &code);
+  if (0 > qs)
+  {
+    GNUNET_break (0 < qs);
+    return (MHD_YES ==
+            TALER_MHD_reply_with_error (gc->connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                        "create_challenge_code (for rate 
limiting)"))
+      ? GNUNET_NO
+      : GNUNET_SYSERR;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    return (MHD_YES ==
+            TALER_MHD_reply_with_error (gc->connection,
+                                        MHD_HTTP_TOO_MANY_REQUESTS,
+                                        TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
+                                        NULL))
+      ? GNUNET_NO
+      : GNUNET_SYSERR;
+  }
+  /* decrement trial counter */
+  ANASTASIS_hash_answer (code + 1,      /* always use wrong answer */
+                         &hc);
+  cs = db->verify_challenge_code (db->cls,
+                                  &gc->truth_uuid,
+                                  &hc,
+                                  &dummy,
+                                  &satisfied);
+  switch (cs)
+  {
+  case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
+    /* good, what we wanted */
+    return GNUNET_OK;
+  case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
+  case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
+    GNUNET_break (0);
+    return (MHD_YES ==
+            TALER_MHD_reply_with_error (gc->connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                        "verify_challenge_code"))
+      ? GNUNET_NO
+      : GNUNET_SYSERR;
+  case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
+    return (MHD_YES ==
+            TALER_MHD_reply_with_error (gc->connection,
+                                        MHD_HTTP_TOO_MANY_REQUESTS,
+                                        TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
+                                        NULL))
+      ? GNUNET_NO
+      : GNUNET_SYSERR;
+  case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
+    /* this should be impossible, we used code+1 */
+    GNUNET_assert (0);
+  }
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Handle special case of a security question where we do not
+ * generate a code. Rate limits answers against brute forcing.
+ *
+ * @param[in,out] gc request to handle
+ * @param decrypted_truth hash to check against
+ * @param decrypted_truth_size number of bytes in @a decrypted_truth
+ * @return MHD status code
+ */
+static MHD_RESULT
+handle_security_question (struct GetContext *gc,
+                          const void *decrypted_truth,
+                          size_t decrypted_truth_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Handling security question challenge\n");
+  if (! gc->have_response)
+  {
+    return TALER_MHD_reply_with_error (gc->connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
+                                       NULL);
+  }
+  /* rate limit */
+  {
+    enum GNUNET_GenericReturnValue ret;
+
+    ret = rate_limit (gc);
+    if (GNUNET_OK != ret)
+      return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+  }
+  /* check reply matches truth */
+  if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
+       (0 != memcmp (&gc->challenge_response,
+                     decrypted_truth,
+                     decrypted_truth_size)) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Wrong answer provided to secure question had %u bytes, wanted 
%u\n",
+                (unsigned int) decrypted_truth_size,
+                (unsigned int) sizeof (struct GNUNET_HashCode));
+    return TALER_MHD_reply_with_error (gc->connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
+                                       NULL);
+  }
+  /* good, return the key share */
+  return return_key_share (&gc->truth_uuid,
+                           gc->connection);
+}
+
+
+/**
+ * Handle special case of an answer being directly checked by the
+ * plugin and not by our database. Rate limits answers against brute
+ * forcing.
+ *
+ * @param[in,out] gc request to handle
+ * @param decrypted_truth hash to check against
+ * @param decrypted_truth_size number of bytes in @a decrypted_truth
+ * @return MHD status code
+ */
+static MHD_RESULT
+direct_validation (struct GetContext *gc,
+                   const void *decrypted_truth,
+                   size_t decrypted_truth_size)
+{
+  /* Non-random code, call plugin directly! */
+  enum ANASTASIS_AUTHORIZATION_Result aar;
+  enum GNUNET_GenericReturnValue res;
+
+  res = rate_limit (gc);
+  if (GNUNET_OK != res)
+    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+  gc->as = gc->authorization->start (gc->authorization->cls,
+                                     &AH_trigger_daemon,
+                                     NULL,
+                                     &gc->truth_uuid,
+                                     0LLU,
+                                     decrypted_truth,
+                                     decrypted_truth_size);
+  if (NULL == gc->as)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (gc->connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
+                                       NULL);
+  }
+  aar = gc->authorization->process (gc->as,
+                                    GNUNET_TIME_UNIT_ZERO_ABS,
+                                    gc->connection);
+  switch (aar)
+  {
+  case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
+    GNUNET_break (0);
+    return MHD_YES;
+  case ANASTASIS_AUTHORIZATION_RES_FAILED:
+    return MHD_YES;
+  case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
+    gc_suspended (gc);
+    return MHD_YES;
+  case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
+    GNUNET_break (0);
+    return MHD_NO;
+  case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
+    return MHD_NO;
+  case ANASTASIS_AUTHORIZATION_RES_FINISHED:
+    return return_key_share (&gc->truth_uuid,
+                             gc->connection);
+  }
+  GNUNET_break (0);
+  return MHD_NO;
+}
+
+
 MHD_RESULT
 AH_handler_truth_get (
   struct MHD_Connection *connection,
@@ -1113,7 +1332,6 @@ AH_handler_truth_get (
           GNUNET_TIME_UNIT_SECONDS);
       }
     }
-
   } /* end of first-time initialization (if NULL == gc) */
   else
   {
@@ -1291,104 +1509,14 @@ AH_handler_truth_get (
      but check that the hash matches */
   if (is_question)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Handling security question challenge\n");
-    if (! gc->have_response)
-    {
-      GNUNET_free (decrypted_truth);
-      GNUNET_free (truth_mime);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_FORBIDDEN,
-                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
-                                         NULL);
-    }
+    MHD_RESULT ret;
 
-    {
-      enum GNUNET_DB_QueryStatus qs;
-      struct GNUNET_TIME_Absolute rt;
-      uint64_t code;
-      enum ANASTASIS_DB_CodeStatus cs;
-      struct GNUNET_HashCode hc;
-      bool satisfied;
-      uint64_t dummy;
-
-      rt = GNUNET_TIME_UNIT_FOREVER_ABS;
-      qs = db->create_challenge_code (db->cls,
-                                      &gc->truth_uuid,
-                                      MAX_QUESTION_FREQ,
-                                      GNUNET_TIME_UNIT_HOURS,
-                                      INITIAL_RETRY_COUNTER,
-                                      &rt,
-                                      &code);
-      if (0 > qs)
-      {
-        GNUNET_break (0 < qs);
-        GNUNET_free (decrypted_truth);
-        GNUNET_free (truth_mime);
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                           "create_challenge_code (for rate 
limiting)");
-      }
-      if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      {
-        GNUNET_free (decrypted_truth);
-        GNUNET_free (truth_mime);
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_TOO_MANY_REQUESTS,
-                                           
TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
-                                           NULL);
-      }
-      /* decrement trial counter */
-      ANASTASIS_hash_answer (code + 1,  /* always use wrong answer */
-                             &hc);
-      cs = db->verify_challenge_code (db->cls,
-                                      &gc->truth_uuid,
-                                      &hc,
-                                      &dummy,
-                                      &satisfied);
-      switch (cs)
-      {
-      case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
-        /* good, what we wanted */
-        break;
-      case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
-      case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
-        GNUNET_break (0);
-        return TALER_MHD_reply_with_error (gc->connection,
-                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                           "verify_challenge_code");
-      case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_TOO_MANY_REQUESTS,
-                                           
TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
-                                           NULL);
-      case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
-        /* this should be impossible, we used code+1 */
-        GNUNET_assert (0);
-      }
-    }
-    if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
-         (0 != memcmp (&gc->challenge_response,
-                       decrypted_truth,
-                       decrypted_truth_size)) )
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Wrong answer provided to secure question had %u bytes, 
wanted %u\n",
-                  (unsigned int) decrypted_truth_size,
-                  (unsigned int) sizeof (struct GNUNET_HashCode));
-      GNUNET_free (decrypted_truth);
-      GNUNET_free (truth_mime);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_FORBIDDEN,
-                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
-                                         NULL);
-    }
-    GNUNET_free (decrypted_truth);
+    ret = handle_security_question (gc,
+                                    decrypted_truth,
+                                    decrypted_truth_size);
     GNUNET_free (truth_mime);
-    return return_key_share (&gc->truth_uuid,
-                             connection);
+    GNUNET_free (decrypted_truth);
+    return ret;
   }
 
   /* Not security question, check for answer in DB */
@@ -1399,6 +1527,18 @@ AH_handler_truth_get (
     uint64_t code;
 
     GNUNET_free (truth_mime);
+    if (gc->authorization->user_provided_code)
+    {
+      MHD_RESULT res;
+
+      res = direct_validation (gc,
+                               decrypted_truth,
+                               decrypted_truth_size);
+      GNUNET_free (decrypted_truth);
+      return res;
+    }
+
+    /* random code, check against database */
     cs = db->verify_challenge_code (db->cls,
                                     &gc->truth_uuid,
                                     &gc->challenge_response,
diff --git a/src/include/anastasis_database_plugin.h 
b/src/include/anastasis_database_plugin.h
index 7bf91a2..cf5a69a 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -633,7 +633,7 @@ struct ANASTASIS_DatabasePlugin
   (*mark_challenge_code_satisfied)(
     void *cls,
     const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
-    const uint64_t code);
+    uint64_t code);
 
 
   /**
diff --git a/src/reducer/anastasis_api_recovery_redux.c 
b/src/reducer/anastasis_api_recovery_redux.c
index 897a6dd..8709cf9 100644
--- a/src/reducer/anastasis_api_recovery_redux.c
+++ b/src/reducer/anastasis_api_recovery_redux.c
@@ -1684,10 +1684,13 @@ select_challenge_cb (void *cls,
       json_object_set_new (sctx->state,
                            "selected_challenge_uuid",
                            GNUNET_JSON_from_data_auto (&cd->uuid)));
-    if (0 == strcmp ("question",
-                     cd->type))
+    if ( (0 == strcmp ("question",
+                       cd->type)) ||
+         (0 == strcmp ("totp",
+                       cd->type)) )
     {
-      /* security question, immediately request user to answer it */
+      /* security question or TOTP:
+         immediately request user to answer it */
       set_state (sctx->state,
                  ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
       sctx->cb (sctx->cb_cls,

-- 
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]