gnunet-svn
[Top][All Lists]
Advanced

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

[taler-anastasis] branch master updated: starting with TOTP plugin for #


From: gnunet
Subject: [taler-anastasis] branch master updated: starting with TOTP plugin for #7023
Date: Sun, 26 Sep 2021 19:24:17 +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 f2dbb45  starting with TOTP plugin for #7023
f2dbb45 is described below

commit f2dbb45c194cfc41395b7a13a828e24ceeb7bc89
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Sep 26 19:24:14 2021 +0200

    starting with TOTP plugin for #7023
---
 contrib/gana                                       |   2 +-
 src/authorization/Makefile.am                      |  21 +-
 .../anastasis_authorization_plugin_file.c          |   4 +-
 ...ile.c => anastasis_authorization_plugin_totp.c} | 304 ++++++++++++---------
 src/include/anastasis_authorization_plugin.h       |   8 +
 5 files changed, 208 insertions(+), 131 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index 323cb82..3b63803 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 323cb8276408e2c02b59bbe6e10da904538a149d
+Subproject commit 3b638032297cfed132912dfe82a1c47033eff03b
diff --git a/src/authorization/Makefile.am b/src/authorization/Makefile.am
index cfcd89e..c99c6b4 100644
--- a/src/authorization/Makefile.am
+++ b/src/authorization/Makefile.am
@@ -69,7 +69,8 @@ plugin_LTLIBRARIES = \
   libanastasis_plugin_authorization_file.la \
   libanastasis_plugin_authorization_iban.la \
   libanastasis_plugin_authorization_post.la \
-  libanastasis_plugin_authorization_sms.la
+  libanastasis_plugin_authorization_sms.la \
+  libanastasis_plugin_authorization_totp.la
 
 
 libanastasis_plugin_authorization_file_la_SOURCES = \
@@ -151,3 +152,21 @@ libanastasis_plugin_authorization_sms_la_LDFLAGS = \
   -ljansson \
   -lmicrohttpd \
   $(XLIB)
+
+
+libanastasis_plugin_authorization_totp_la_SOURCES = \
+  anastasis_authorization_plugin_totp.c
+libanastasis_plugin_authorization_totp_la_LIBADD = \
+  $(LTLIBINTL)
+libanastasis_plugin_authorization_totp_la_LDFLAGS = \
+  $(ANASTASIS_PLUGIN_LDFLAGS) \
+  $(top_builddir)/src/stasis/libanastasisdb.la \
+  -ltalerjson \
+  -ltalermhd \
+  -ltalerutil \
+  -lgnunetjson \
+  -lgnunetutil \
+  -ljansson \
+  -lmicrohttpd \
+  -lgcrypt \
+  $(XLIB)
diff --git a/src/authorization/anastasis_authorization_plugin_file.c 
b/src/authorization/anastasis_authorization_plugin_file.c
index 66dbbe1..d41eaa3 100644
--- a/src/authorization/anastasis_authorization_plugin_file.c
+++ b/src/authorization/anastasis_authorization_plugin_file.c
@@ -86,7 +86,7 @@ file_validate (void *cls,
 
   (void) cls;
   if (NULL == data)
-    return GNUNET_NO;
+    return GNUNET_SYSERR;
   filename = GNUNET_STRINGS_data_to_string_alloc (data,
                                                   data_length);
   flag = false;
@@ -100,7 +100,7 @@ file_validate (void *cls,
     }
   }
   if (flag)
-    return GNUNET_NO;
+    return GNUNET_SYSERR;
   GNUNET_free (filename);
   return GNUNET_OK;
 }
diff --git a/src/authorization/anastasis_authorization_plugin_file.c 
b/src/authorization/anastasis_authorization_plugin_totp.c
similarity index 52%
copy from src/authorization/anastasis_authorization_plugin_file.c
copy to src/authorization/anastasis_authorization_plugin_totp.c
index 66dbbe1..6fcdd39 100644
--- a/src/authorization/anastasis_authorization_plugin_file.c
+++ b/src/authorization/anastasis_authorization_plugin_totp.c
@@ -1,6 +1,6 @@
 /*
-  This file is part of Anastasis
-  Copyright (C) 2019 Anastasis SARL
+  This totp is part of Anastasis
+  Copyright (C) 2021 Anastasis SARL
 
   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
@@ -11,24 +11,42 @@
   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
 
   You should have received a copy of the GNU Affero General Public License 
along with
-  Anastasis; see the file COPYING.GPL.  If not, see 
<http://www.gnu.org/licenses/>
+  Anastasis; see the totp COPYING.GPL.  If not, see 
<http://www.gnu.org/licenses/>
 */
 /**
- * @file anastasis_authorization_plugin_file.c
- * @brief authorization plugin file based for testing
- * @author Dominik Meister
+ * @totp anastasis_authorization_plugin_totp.c
+ * @brief authorization plugin using totp
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include "anastasis_authorization_plugin.h"
 #include <taler/taler_mhd_lib.h>
 #include <gnunet/gnunet_db_lib.h>
 #include "anastasis_database_lib.h"
+#include <gcrypt.h>
+
 
 /**
  * How many retries do we allow per code?
  */
 #define INITIAL_RETRY_COUNTER 3
 
+/**
+ * How long is a TOTP code valid?
+ */
+#define TOTP_VALIDITY_PERIOD GNUNET_TIME_relative_multiply ( \
+    GNUNET_TIME_UNIT_SECONDS, 30)
+
+/**
+ * Range of time we allow (plus-minus).
+ */
+#define TIME_INTERVAL_RANGE 2
+
+/**
+ * How long is the shared secret in bytes?
+ */
+#define SECRET_LEN 32
+
 
 /**
  * Saves the state of a authorization process
@@ -41,19 +59,15 @@ struct ANASTASIS_AUTHORIZATION_State
   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
 
   /**
-   * Code which is sent to the user (here saved into a file)
+   * Our context.
    */
-  uint64_t code;
+  const struct ANASTASIS_AuthorizationContext *ac;
 
   /**
-   * holds the truth information
+   * Was the challenge satisfied?
    */
-  char *filename;
+  bool ok;
 
-  /**
-   * closure
-   */
-  void *cls;
 };
 
 
@@ -75,54 +89,137 @@ struct ANASTASIS_AUTHORIZATION_State
  *         #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on 
@a connection
  */
 static enum GNUNET_GenericReturnValue
-file_validate (void *cls,
+totp_validate (void *cls,
                struct MHD_Connection *connection,
                const char *truth_mime,
                const char *data,
                size_t data_length)
 {
-  char *filename;
-  bool flag;
-
   (void) cls;
+  (void) truth_mime;
+  (void) connection;
   if (NULL == data)
-    return GNUNET_NO;
-  filename = GNUNET_STRINGS_data_to_string_alloc (data,
-                                                  data_length);
-  flag = false;
-  for (size_t i = 0; i<strlen (filename); i++)
   {
-    if ( (filename[i] == ' ') ||
-         (filename[i] == '/') )
-    {
-      flag = true;
-      break;
-    }
+    GNUNET_break_op (0);
+    if (MHD_NO ==
+        TALER_MHD_reply_with_error (connection,
+                                    MHD_HTTP_EXPECTATION_FAILED,
+                                    TALER_EC_ANASTASIS_TOTP_KEY_MISSING,
+                                    NULL))
+      return GNUNET_SYSERR;
+    return GNUNET_NO;
   }
-  if (flag)
+  if (SECRET_LEN != data_length)
+  {
+    GNUNET_break_op (0);
+    if (MHD_NO ==
+        TALER_MHD_reply_with_error (connection,
+                                    MHD_HTTP_EXPECTATION_FAILED,
+                                    TALER_EC_ANASTASIS_TOTP_KEY_INVALID,
+                                    NULL))
+      return GNUNET_SYSERR;
     return GNUNET_NO;
-  GNUNET_free (filename);
+  }
   return GNUNET_OK;
 }
 
 
+/**
+ * Compute TOTP code at current time with offset
+ * @a time_off for the @a key.
+ *
+ * @param time_off offset to apply when computing the code
+ * @param key input key material
+ * @param key_size number of bytes in @a key
+ * @return TOTP code at this time
+ */
+static uint64_t
+compute_totp (int time_off,
+              const void *key,
+              size_t key_size)
+{
+  struct GNUNET_TIME_Absolute now;
+  time_t t;
+  uint64_t ctr;
+  uint8_t hmac[16]; /* SHA1: 16 bytes */
+
+  now = GNUNET_TIME_absolute_get ();
+  while (time_off < 0)
+  {
+    now = GNUNET_TIME_absolute_subtract (now,
+                                         TOTP_VALIDITY_PERIOD);
+    time_off++;
+  }
+  while (time_off > 0)
+  {
+    now = GNUNET_TIME_absolute_add (now,
+                                    TOTP_VALIDITY_PERIOD);
+    time_off--;
+  }
+  t = now.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+  ctr = GNUNET_htonll (t / 30LLU);
+
+  {
+    gcry_md_hd_t md;
+    const unsigned char *mc;
+
+    GNUNET_assert (GPG_ERR_NO_ERROR ==
+                   gcry_md_open (&md,
+                                 GCRY_MD_SHA1,
+                                 GCRY_MD_FLAG_HMAC));
+    gcry_md_setkey (md,
+                    key,
+                    key_size);
+    gcry_md_write (md,
+                   &ctr,
+                   sizeof (ctr));
+    mc = gcry_md_read (md,
+                       GCRY_MD_SHA1);
+    GNUNET_assert (NULL != mc);
+    memcpy (hmac,
+            mc,
+            sizeof (hmac));
+    gcry_md_close (md);
+  }
+
+  {
+    uint32_t code = 0;
+
+    for (int count = 0; count < 4; count++)
+      code += hmac[(hmac[sizeof (hmac) - 1] & 0x0f) + 3 - count] << 8 * count;
+    code &= 0x7fffffff;
+
+#if VAR_DIGITS
+    if (digits == 6)
+      code = code % 1000000;
+    else if (digits == 7)
+      code = code % 10000000;
+    else if (digits == 8)
+      code = code % 100000000;
+#else
+    code = code % 1000000;
+#endif
+    return code;
+  }
+}
+
+
 /**
  * Begin issuing authentication challenge to user based on @a data.
- * I.e. start to send SMS or e-mail or launch video identification.
  *
  * @param cls closure
  * @param trigger function to call when we made progress
  * @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 secret code that the user has to provide back to satisfy the 
challenge in
+ * @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. is it a valid phone number, etc.)
+ * @param data 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
  */
 static struct ANASTASIS_AUTHORIZATION_State *
-file_start (void *cls,
+totp_start (void *cls,
             GNUNET_SCHEDULER_TaskCallback trigger,
             void *trigger_cls,
             const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
@@ -132,33 +229,27 @@ file_start (void *cls,
 {
   const struct ANASTASIS_AuthorizationContext *ac = cls;
   struct ANASTASIS_AUTHORIZATION_State *as;
-  enum GNUNET_DB_QueryStatus qs;
-
-  /* If the user can show this challenge code, this
-     plugin is already happy (no additional
-     requirements), so mark this challenge as
-     already satisfied from the start. */
-  qs = ac->db->mark_challenge_code_satisfied (ac->db->cls,
-                                              truth_uuid,
-                                              code);
-  if (qs <= 0)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
+  uint64_t want;
+
   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
-  as->cls = cls;
+  as->ac = ac;
   as->truth_uuid = *truth_uuid;
-  as->code = code;
-  as->filename = GNUNET_strndup (data,
-                                 data_length);
+  for (int i = -TIME_INTERVAL_RANGE;
+       i < TIME_INTERVAL_RANGE;
+       i++)
+  {
+    want = compute_totp (i,
+                         data,
+                         data_length);
+    if (code == want)
+      as->ok = true;
+  }
   return as;
 }
 
 
 /**
  * Begin issuing authentication challenge to user based on @a data.
- * I.e. start to send SMS or e-mail or launch video identification.
  *
  * @param as authorization state
  * @param timeout how long do we have to produce a reply
@@ -166,13 +257,16 @@ file_start (void *cls,
  * @return state of the request
  */
 static enum ANASTASIS_AUTHORIZATION_Result
-file_process (struct ANASTASIS_AUTHORIZATION_State *as,
+totp_process (struct ANASTASIS_AUTHORIZATION_State *as,
               struct GNUNET_TIME_Absolute timeout,
               struct MHD_Connection *connection)
 {
+  MHD_RESULT mres;
   const char *mime;
   const char *lang;
 
+  if (as->ok)
+    return ANASTASIS_AUTHORIZATION_RES_FINISHED;
   mime = MHD_lookup_connection_value (connection,
                                       MHD_HEADER_KIND,
                                       MHD_HTTP_HEADER_ACCEPT);
@@ -183,91 +277,47 @@ file_process (struct ANASTASIS_AUTHORIZATION_State *as,
                                       MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
   if (NULL == lang)
     lang = "en";
-  {
-    FILE *f = fopen (as->filename, "w");
-
-    if (NULL == f)
-    {
-      struct MHD_Response *resp;
-      MHD_RESULT mres;
-
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                                "open",
-                                as->filename);
-      resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                                   "open");
-      mres = MHD_queue_response (connection,
-                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                 resp);
-      MHD_destroy_response (resp);
-      if (MHD_YES != mres)
-        return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
-      return ANASTASIS_AUTHORIZATION_RES_FAILED;
-    }
-
-    /* print challenge code to file */
-    if (0 >= fprintf (f,
-                      "%lu",
-                      as->code))
-    {
-      struct MHD_Response *resp;
-      MHD_RESULT mres;
-
-      GNUNET_break (0 == fclose (f));
-      resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                                   "write");
-      mres = MHD_queue_response (connection,
-                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                 resp);
-      MHD_destroy_response (resp);
-      if (MHD_YES != mres)
-        return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
-      return ANASTASIS_AUTHORIZATION_RES_FAILED;
-    }
-    GNUNET_break (0 == fclose (f));
-  }
 
   /* Build HTTP response */
   {
     struct MHD_Response *resp;
+    struct GNUNET_TIME_Absolute now;
 
+    now = GNUNET_TIME_absolute_get ();
     if (TALER_MHD_xmime_matches (mime,
                                  "application/json"))
     {
       resp = TALER_MHD_MAKE_JSON_PACK (
-        GNUNET_JSON_pack_string ("filename",
-                                 as->filename));
+        GNUNET_JSON_pack_time_abs ("server_time",
+                                   now));
     }
     else
     {
       size_t response_size;
       char *response;
 
-      response_size = GNUNET_asprintf (&response,
-                                       _ ("Challenge written to file"));
+      // FIXME: i18n of the message based on 'lang' ...
+      response_size
+        = GNUNET_asprintf (&response,
+                           "Server time: %s",
+                           GNUNET_STRINGS_absolute_time_to_string (now));
       resp = MHD_create_response_from_buffer (response_size,
                                               response,
                                               MHD_RESPMEM_MUST_COPY);
-      GNUNET_free (response);
       TALER_MHD_add_global_headers (resp);
       GNUNET_break (MHD_YES ==
                     MHD_add_response_header (resp,
                                              MHD_HTTP_HEADER_CONTENT_TYPE,
                                              "text/plain"));
     }
-
-    {
-      MHD_RESULT mres;
-
-      mres = MHD_queue_response (connection,
-                                 MHD_HTTP_FORBIDDEN,
-                                 resp);
-      MHD_destroy_response (resp);
-      if (MHD_YES != mres)
-        return ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED;
-      return ANASTASIS_AUTHORIZATION_RES_SUCCESS;
-    }
+    mres = MHD_queue_response (connection,
+                               MHD_HTTP_FORBIDDEN,
+                               resp);
+    MHD_destroy_response (resp);
   }
+  if (MHD_YES != mres)
+    return ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED;
+  return ANASTASIS_AUTHORIZATION_RES_SUCCESS;
 }
 
 
@@ -277,35 +327,35 @@ file_process (struct ANASTASIS_AUTHORIZATION_State *as,
  * @param as state to clean up
  */
 static void
-file_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
+totp_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
 {
-  GNUNET_free (as->filename);
   GNUNET_free (as);
 }
 
 
 /**
- * Initialize File based authorization plugin
+ * Initialize Totp based authorization plugin
  *
  * @param cls a configuration instance
  * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin`
  */
 void *
-libanastasis_plugin_authorization_file_init (void *cls)
+libanastasis_plugin_authorization_totp_init (void *cls)
 {
   const struct ANASTASIS_AuthorizationContext *ac = cls;
   struct ANASTASIS_AuthorizationPlugin *plugin;
 
   plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
   plugin->cls = (void *) ac;
+  plugin->user_provided_code = true;
   plugin->retry_counter = INITIAL_RETRY_COUNTER;
-  plugin->code_validity_period = GNUNET_TIME_UNIT_MINUTES;
-  plugin->code_rotation_period = GNUNET_TIME_UNIT_MINUTES;
-  plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_MINUTES;
-  plugin->validate = &file_validate;
-  plugin->start = &file_start;
-  plugin->process = &file_process;
-  plugin->cleanup = &file_cleanup;
+  plugin->code_validity_period = TOTP_VALIDITY_PERIOD;
+  plugin->code_rotation_period = plugin->code_validity_period;
+  plugin->code_retransmission_frequency = plugin->code_validity_period;
+  plugin->validate = &totp_validate;
+  plugin->start = &totp_start;
+  plugin->process = &totp_process;
+  plugin->cleanup = &totp_cleanup;
   return plugin;
 }
 
@@ -317,7 +367,7 @@ libanastasis_plugin_authorization_file_init (void *cls)
  * @return NULL (always)
  */
 void *
-libanastasis_plugin_authorization_file_done (void *cls)
+libanastasis_plugin_authorization_totp_done (void *cls)
 {
   struct ANASTASIS_AuthorizationPlugin *plugin = cls;
 
diff --git a/src/include/anastasis_authorization_plugin.h 
b/src/include/anastasis_authorization_plugin.h
index 91a88f8..48d8741 100644
--- a/src/include/anastasis_authorization_plugin.h
+++ b/src/include/anastasis_authorization_plugin.h
@@ -126,6 +126,14 @@ struct ANASTASIS_AuthorizationPlugin
    */
   bool payment_plugin_managed;
 
+  /**
+   * The plugin expects the "code" in the "start" function to be
+   * provided by the user and not generated by the Anastasis
+   * backend. The plugin will then validate the code using its own
+   * means.  Used by TOTP.
+   */
+  bool user_provided_code;
+
   /**
    * How often are retries allowed for challenges created
    * by this plugin?

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