summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-09-29 23:32:06 +0200
committerChristian Grothoff <christian@grothoff.org>2021-09-29 23:32:06 +0200
commitba92d27e5901f5063439914fbce99543cce74238 (patch)
tree30e5724f3fd114e18c34e64ca1361f3058f6ce2d
parent34bf2607b36806d4c21fc0a706e688c65009ca5a (diff)
downloadanastasis-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.glade53
-rw-r--r--src/anastasis/Makefile.am1
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-totp.c156
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-video.c70
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.
-&lt;b&gt;Make sure to scan the QR code before closing this dialog!&lt;/b&gt;</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;