diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-09-29 23:32:06 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-09-29 23:32:06 +0200 |
commit | ba92d27e5901f5063439914fbce99543cce74238 (patch) | |
tree | 30e5724f3fd114e18c34e64ca1361f3058f6ce2d | |
parent | 34bf2607b36806d4c21fc0a706e688c65009ca5a (diff) | |
download | anastasis-gtk-ba92d27e5901f5063439914fbce99543cce74238.tar.gz anastasis-gtk-ba92d27e5901f5063439914fbce99543cce74238.tar.bz2 anastasis-gtk-ba92d27e5901f5063439914fbce99543cce74238.zip |
force user to enter TOTP code once before proceeding during backup
-rw-r--r-- | contrib/anastasis_gtk_auth_add_totp.glade | 53 | ||||
-rw-r--r-- | src/anastasis/Makefile.am | 1 | ||||
-rw-r--r-- | src/anastasis/anastasis-gtk_handle-method-totp.c | 156 | ||||
-rw-r--r-- | src/anastasis/anastasis-gtk_handle-method-video.c | 70 |
4 files changed, 210 insertions, 70 deletions
diff --git a/contrib/anastasis_gtk_auth_add_totp.glade b/contrib/anastasis_gtk_auth_add_totp.glade index 0809bcc..1d1ce64 100644 --- a/contrib/anastasis_gtk_auth_add_totp.glade +++ b/contrib/anastasis_gtk_auth_add_totp.glade @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.38.2 +<!-- Generated with glade 3.38.2 Copyright (C) 2019-2021 Anastasis SARL @@ -60,7 +60,7 @@ Author: Belen Pena, Christian Grothoff, Dennis Neufeld <object class="GtkButton" id="anastasis_gtk_b_totp_dialog_btn_ok"> <property name="label">gtk-ok</property> <property name="visible">True</property> - <property name="sensitive">True</property> + <property name="sensitive">False</property> <property name="can-focus">False</property> <property name="can-default">True</property> <property name="has-default">True</property> @@ -93,8 +93,7 @@ Author: Belen Pena, Christian Grothoff, Dennis Neufeld <property name="can-focus">False</property> <property name="label" translatable="yes">For TOTP authentication, you need to set a name for the TOTP secret. Then, you must scan the generated QR code with your TOTP App to -import the TOTP secret it into your TOTP App. -<b>Make sure to scan the QR code before closing this dialog!</b></property> +import the TOTP secret it into your TOTP App.</property> <property name="use-markup">True</property> </object> <packing> @@ -173,6 +172,52 @@ import the TOTP secret it into your TOTP App. <property name="expand">False</property> <property name="fill">False</property> <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Confirm that your TOTP App works by entering the current code here:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="totp_entry"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="tooltip-text" translatable="yes">Enter the 8-digit TOTP code. Valid for 30s.</property> + <property name="max-length">9</property> + <property name="activates-default">True</property> + <property name="max-width-chars">9</property> + <property name="primary-icon-stock">gtk-dialog-question</property> + <property name="placeholder-text" translatable="yes">00000000</property> + <property name="input-purpose">number</property> + <signal name="changed" handler="totp_entry_changed_cb" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> <property name="position">2</property> </packing> </child> diff --git a/src/anastasis/Makefile.am b/src/anastasis/Makefile.am index 4e409ab..b27a8af 100644 --- a/src/anastasis/Makefile.am +++ b/src/anastasis/Makefile.am @@ -74,6 +74,7 @@ anastasis_gtk_LDADD = \ -lgnunetcurl \ -lgnunetjson \ -ljansson \ + -lgcrypt \ -lmicrohttpd \ -lmagic \ $(INTLLIBS) diff --git a/src/anastasis/anastasis-gtk_handle-method-totp.c b/src/anastasis/anastasis-gtk_handle-method-totp.c index 34cdbc4..b85e6d4 100644 --- a/src/anastasis/anastasis-gtk_handle-method-totp.c +++ b/src/anastasis/anastasis-gtk_handle-method-totp.c @@ -29,12 +29,98 @@ #include "anastasis-gtk_helper.h" #include "anastasis-gtk_handle-identity-changed.h" #include <jansson.h> +#include <gcrypt.h> +/** + * 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 /** * Random secret used in the current dialog. */ -static char totp[32]; +static char totp_key[SECRET_LEN]; + + +/** + * 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 + * @return TOTP code at this time + */ +static uint64_t +compute_totp (int time_off) +{ + struct GNUNET_TIME_Absolute now; + time_t t; + uint64_t ctr; + uint8_t hmac[20]; /* SHA1: 20 bytes */ + + now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); + 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, + totp_key, + sizeof (totp_key)); + 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; + int offset; + + offset = hmac[sizeof (hmac) - 1] & 0x0f; + for (int count = 0; count < 4; count++) + code |= hmac[offset + 3 - count] << (8 * count); + code &= 0x7fffffff; + /* always use 8 digits (maximum) */ + code = code % 100000000; + return code; + } +} /** @@ -100,20 +186,20 @@ refresh_totp (GtkBuilder *builder) const char *name; char *u_name; char *uri; - char base_sec[sizeof (totp) * 2]; + char base_sec[sizeof (totp_key) * 2]; GdkPixbuf *pb; GtkImage *img; gtk_widget_set_sensitive ( GTK_WIDGET (gtk_builder_get_object (builder, "anastasis_gtk_b_totp_dialog_btn_ok")), - false); + FALSE); q = GTK_ENTRY (gtk_builder_get_object (builder, "anastasis_gtk_b_totp_dialog_name_entry")); name = gtk_entry_get_text (q); u_name = TALER_urlencode (name); - base32enc (totp, - sizeof (totp), + base32enc (totp_key, + sizeof (totp_key), base_sec); GNUNET_asprintf (&uri, "otpauth://totp/%s?digits=8&secret=%s", @@ -130,10 +216,6 @@ refresh_totp (GtkBuilder *builder) gtk_image_set_from_pixbuf (img, pb); g_object_unref (pb); - gtk_widget_set_sensitive ( - GTK_WIDGET (gtk_builder_get_object (builder, - "anastasis_gtk_b_totp_dialog_btn_ok")), - true); } } @@ -169,15 +251,15 @@ anastasis_gtk_b_totp_dialog_response_cb (GtkDialog *dialog, "type", "totp", "challenge", - GNUNET_JSON_from_data (totp, - sizeof (totp)), + GNUNET_JSON_from_data (totp_key, + sizeof (totp_key)), "instructions", name); gtk_widget_destroy (GTK_WIDGET (dialog)); g_object_unref (G_OBJECT (builder)); - memset (totp, + memset (totp_key, 0, - sizeof (totp)); + sizeof (totp_key)); AG_freeze (); AG_ra = ANASTASIS_redux_action (AG_redux_state, "add_authentication", @@ -189,11 +271,55 @@ anastasis_gtk_b_totp_dialog_response_cb (GtkDialog *dialog, void +totp_entry_changed_cb (GtkEntry *entry, + gpointer user_data) +{ + GtkBuilder *builder = GTK_BUILDER (user_data); + GtkWidget *but; + GtkEntry *q; + const char *code; + unsigned int val; + char dummy; + bool found = false; + + q = GTK_ENTRY (gtk_builder_get_object (builder, + "totp_entry")); + code = gtk_entry_get_text (q); + if (1 != sscanf (code, + "%u%c", + &val, + &dummy)) + return; + for (int i = -TIME_INTERVAL_RANGE; + i <= TIME_INTERVAL_RANGE; + i++) + { + if (val == compute_totp (i)) + { + found = true; + break; + } + } + if (! found) + return; + but = GTK_WIDGET (gtk_builder_get_object (builder, + "anastasis_gtk_b_totp_dialog_btn_ok")); + gtk_widget_set_sensitive (but, + TRUE); +} + + +void anastasis_gtk_b_totp_dialog_name_entry_changed_cb (GtkEntry *entry, gpointer user_data) { GtkBuilder *builder = GTK_BUILDER (user_data); + GtkEntry *e; + /* clear code user already entered, if any */ + e = GTK_ENTRY (gtk_builder_get_object (builder, + "totp_entry")); + gtk_entry_set_text (e, ""); refresh_totp (builder); } @@ -212,8 +338,8 @@ anastasis_gtk_btn_add_auth_totp_clicked_cb (GObject *object, GtkBuilder *builder; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - totp, - sizeof (totp)); + totp_key, + sizeof (totp_key)); builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_totp.glade", NULL); if (NULL == builder) diff --git a/src/anastasis/anastasis-gtk_handle-method-video.c b/src/anastasis/anastasis-gtk_handle-method-video.c index 64841b8..a0a6053 100644 --- a/src/anastasis/anastasis-gtk_handle-method-video.c +++ b/src/anastasis/anastasis-gtk_handle-method-video.c @@ -19,9 +19,11 @@ */ /** - * @file src/anastasis/anastasis-gtk_handle-method-question.c - * @brief Handle dialogs for security question + * @file src/anastasis/anastasis-gtk_handle-method-video.c + * @brief Handle dialogs for video authentication * @author Christian Grothoff + * + * FIXME: This implementation is far from complete. */ #include <gnunet/platform.h> #include <gnunet/gnunet_util_lib.h> @@ -39,15 +41,11 @@ * @param user_data the builder of the dialog */ void -anastasis_gtk_b_question_dialog_response_cb (GtkDialog *dialog, - gint response_id, - gpointer user_data) +anastasis_gtk_b_video_dialog_response_cb (GtkDialog *dialog, + gint response_id, + gpointer user_data) { GtkBuilder *builder = GTK_BUILDER (user_data); - GtkEntry *q; - GtkEntry *a; - const char *qs; - const char *as; json_t *args; if (GTK_RESPONSE_OK != response_id) @@ -56,19 +54,13 @@ anastasis_gtk_b_question_dialog_response_cb (GtkDialog *dialog, g_object_unref (G_OBJECT (builder)); return; } - q = GTK_ENTRY (gtk_builder_get_object (builder, - "anastasis_gtk_b_question_dialog_question_entry")); - qs = gtk_entry_get_text (q); - a = GTK_ENTRY (gtk_builder_get_object (builder, - "anastasis_gtk_b_question_dialog_answer_entry")); - as = gtk_entry_get_text (a); args = json_pack ("{ s:{s:s, s:o, s:s}}", "authentication_method", "type", - "question", + "video", "challenge", - GNUNET_JSON_from_data (as, - strlen (as)), + GNUNET_JSON_from_data ("DATA", + strlen ("DATA")), "instructions", qs); gtk_widget_destroy (GTK_WIDGET (dialog)); @@ -86,40 +78,16 @@ anastasis_gtk_b_question_dialog_response_cb (GtkDialog *dialog, static void update_sensitivity (GtkBuilder *builder) { - GtkEntry *q; - GtkEntry *a; - const char *qs; - const char *as; - - q = GTK_ENTRY (gtk_builder_get_object (builder, - "anastasis_gtk_b_question_dialog_question_entry")); - qs = gtk_entry_get_text (q); - a = GTK_ENTRY (gtk_builder_get_object (builder, - "anastasis_gtk_b_question_dialog_answer_entry")); - as = gtk_entry_get_text (a); gtk_widget_set_sensitive ( GTK_WIDGET (gtk_builder_get_object (builder, - "anastasis_gtk_b_question_dialog_btn_ok")), - ( (NULL != qs) && - (0 < strlen (qs)) && - (NULL != as) && - (0 < strlen (as)) )); + "anastasis_gtk_b_video_dialog_btn_ok")), + FALSE); } void -anastasis_gtk_b_question_dialog_question_entry_changed_cb (GtkEntry *entry, - gpointer user_data) -{ - GtkBuilder *builder = GTK_BUILDER (user_data); - - update_sensitivity (builder); -} - - -void -anastasis_gtk_b_question_dialog_answer_entry_changed_cb (GtkEntry *entry, - gpointer user_data) +anastasis_gtk_b_video_dialog_video_entry_changed_cb (GtkEntry *entry, + gpointer user_data) { GtkBuilder *builder = GTK_BUILDER (user_data); @@ -128,19 +96,19 @@ anastasis_gtk_b_question_dialog_answer_entry_changed_cb (GtkEntry *entry, /** - * Callback invoked if the the "secure question"-button is clicked. + * Callback invoked if the the "secure video"-button is clicked. * * @param object * @param user_data unused */ void -anastasis_gtk_btn_add_auth_question_clicked_cb (GObject *object, - gpointer user_data) +anastasis_gtk_btn_add_auth_video_clicked_cb (GObject *object, + gpointer user_data) { GtkWidget *ad; GtkBuilder *builder; - builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_question.glade", + builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_video.glade", NULL); if (NULL == builder) { @@ -148,7 +116,7 @@ anastasis_gtk_btn_add_auth_question_clicked_cb (GObject *object, return; } ad = GTK_WIDGET (gtk_builder_get_object (builder, - "anastasis_gtk_b_question_dialog")); + "anastasis_gtk_b_video_dialog")); { GtkWidget *toplevel; |