[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-anastasis] branch master updated: starting with TOTP plugin for #7023,
gnunet <=