summaryrefslogtreecommitdiff
path: root/src/anastasis
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-09-26 15:36:34 +0200
committerChristian Grothoff <christian@grothoff.org>2021-09-26 15:36:34 +0200
commitc07ae7d8d5f39a94b3792c9267d8463746f79ba8 (patch)
treec8be56869f85f2581e75fbf8f9a5f527537355f2 /src/anastasis
parentc03840e32b6ac72497d317c3646304b30a9e9793 (diff)
downloadanastasis-gtk-c07ae7d8d5f39a94b3792c9267d8463746f79ba8.tar.gz
anastasis-gtk-c07ae7d8d5f39a94b3792c9267d8463746f79ba8.tar.bz2
anastasis-gtk-c07ae7d8d5f39a94b3792c9267d8463746f79ba8.zip
preliminary totp support for anastasis-gtk
Diffstat (limited to 'src/anastasis')
-rw-r--r--src/anastasis/Makefile.am1
-rw-r--r--src/anastasis/anastasis-gtk_action.c197
-rw-r--r--src/anastasis/anastasis-gtk_handle-method-totp.c235
-rw-r--r--src/anastasis/anastasis-gtk_helper.c133
-rw-r--r--src/anastasis/anastasis-gtk_helper.h15
5 files changed, 424 insertions, 157 deletions
diff --git a/src/anastasis/Makefile.am b/src/anastasis/Makefile.am
index b137fc1..aabc843 100644
--- a/src/anastasis/Makefile.am
+++ b/src/anastasis/Makefile.am
@@ -44,6 +44,7 @@ anastasis_gtk_SOURCES = \
anastasis-gtk_handle-method-post.c \
anastasis-gtk_handle-method-question.c \
anastasis-gtk_handle-method-sms.c \
+ anastasis-gtk_handle-method-totp.c \
anastasis-gtk_handle-payqr-selection-changed.c \
anastasis-gtk_handle-policy-activate.c \
anastasis-gtk_handle-policy-button.c \
diff --git a/src/anastasis/anastasis-gtk_action.c b/src/anastasis/anastasis-gtk_action.c
index b3c5333..5d4b461 100644
--- a/src/anastasis/anastasis-gtk_action.c
+++ b/src/anastasis/anastasis-gtk_action.c
@@ -1334,143 +1334,6 @@ action_secret_editing (void)
}
-/**
- * Create the QR code image for our zone.
- *
- * @param scale factor for scaling up the size of the image to create
- * @param text text to encode
- * @return NULL on error
- */
-static GdkPixbuf *
-create_qrcode (unsigned int scale,
- const char *text,
- size_t text_size)
-{
- QRinput *qri;
- QRcode *qrc;
- GdkPixbuf *pb;
- guchar *pixels;
- int n_channels;
- const char *dir;
- char *fn;
- unsigned int size;
-
- qri = QRinput_new2 (0, QR_ECLEVEL_M);
- if (NULL == qri)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2");
- return NULL;
- }
- /* first try encoding as uppercase-only alpha-numerical
- QR code (much smaller encoding); if that fails, also
- try using binary encoding (in case nick contains
- special characters). */
- if ((0 != QRinput_append (qri,
- QR_MODE_AN,
- text_size,
- (unsigned char *) text)) &&
- (0 != QRinput_append (qri,
- QR_MODE_8,
- text_size,
- (unsigned char *) text)))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "QRinput_append");
- return NULL;
- }
- qrc = QRcode_encodeInput (qri);
- if (NULL == qrc)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "QRcode_encodeInput");
- QRinput_free (qri);
- return NULL;
- }
- /* We use a trick to create a pixbuf in a way that works for both Gtk2 and
- Gtk3 by loading a dummy file from disk; all other methods are not portable
- to both Gtk2 and Gtk3. */
- dir = GNUNET_GTK_get_data_dir ();
- GNUNET_asprintf (&fn,
- "%s%s",
- dir,
- "qr_dummy.png");
- size = (qrc->width + 8) * scale;
- size += 8 - (size % 8);
- pb = gdk_pixbuf_new_from_file_at_size (fn,
- size,
- size,
- NULL);
- GNUNET_free (fn);
- if (NULL == pb)
- {
- QRcode_free (qrc);
- QRinput_free (qri);
- return NULL;
- }
- pixels = gdk_pixbuf_get_pixels (pb);
- n_channels = gdk_pixbuf_get_n_channels (pb);
- for (unsigned int x = 4 * scale; x < size - 4 * scale; x++)
- for (unsigned int y = 4 * scale; y < size - 4 * scale; y++)
- {
- unsigned int xx = x - 4 * scale;
- unsigned int yy = y - 4 * scale;
- unsigned int ss = size - 8 * scale;
- unsigned int off =
- (xx * qrc->width / ss) + (yy * qrc->width / ss) * qrc->width;
- for (int c = 0; c < n_channels; c++)
- pixels[(y * size + x) * n_channels + c] =
- (0 == (qrc->data[off] & 1)) ? 0xFF : 0;
- }
- QRcode_free (qrc);
- QRinput_free (qri);
- return pb;
-}
-
-
-/**
- * Create the QR code image for our zone.
- *
- * @param text text to encode
- * @return NULL on error
- */
-static GdkPixbuf *
-setup_qrcode (const char *widget,
- const char *text,
- size_t text_size)
-{
- GtkWidget *image;
- GdkScreen *screen;
- GtkSettings *settings;
- gint dpi;
- int scale;
-
- image = GTK_WIDGET (GCG_get_main_window_object (widget));
- if (NULL == image)
- {
- GNUNET_break (0);
- return NULL;
- }
- /* adjust scale to screen resolution */
- screen = gtk_widget_get_screen (GTK_WIDGET (image));
- settings = gtk_settings_get_for_screen (screen);
- g_object_get (G_OBJECT (settings),
- "gtk-xft-dpi",
- &dpi,
- NULL);
- if (-1 == dpi)
- scale = 2;
- else if (dpi >= 122800)
- scale = 4;
- else if (dpi >= 98304)
- scale = 3;
- else
- scale = 2;
- return create_qrcode (3 * scale,
- text,
- text_size);
-}
-
-
static void
action_truths_paying (void)
{
@@ -1489,15 +1352,22 @@ action_truths_paying (void)
{
const char *payto = json_string_value (pt);
GdkPixbuf *pb;
+ GtkWidget *w;
+ w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
+ if (NULL == w)
+ {
+ GNUNET_break (0);
+ continue;
+ }
if (NULL == payto)
{
GNUNET_break (0);
continue;
}
- pb = setup_qrcode ("unpaid_qr_treeview",
- payto,
- strlen (payto));
+ pb = AG_setup_qrcode (w,
+ payto,
+ strlen (payto));
if (NULL == pb)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -1569,7 +1439,14 @@ action_policies_paying (void)
GNUNET_JSON_spec_end ()
};
GdkPixbuf *pb;
+ GtkWidget *w;
+ w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
+ if (NULL == w)
+ {
+ GNUNET_break (0);
+ continue;
+ }
if (GNUNET_OK !=
GNUNET_JSON_parse (ppr,
spec,
@@ -1578,9 +1455,9 @@ action_policies_paying (void)
GNUNET_break (0);
continue;
}
- pb = setup_qrcode ("unpaid_qr_treeview",
- payto,
- strlen (payto));
+ pb = AG_setup_qrcode (w,
+ payto,
+ strlen (payto));
if (NULL == pb)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -1978,9 +1855,13 @@ show_challenge_feedback (void)
}
if (NULL != taler_pay_uri)
{
- qr = setup_qrcode ("anastasis_gtk_challenge_status_treeview",
- taler_pay_uri,
- strlen (taler_pay_uri));
+ GtkWidget*w;
+
+ w = GTK_WIDGET (GCG_get_main_window_object (
+ "anastasis_gtk_challenge_status_treeview"));
+ qr = AG_setup_qrcode (w,
+ taler_pay_uri,
+ strlen (taler_pay_uri));
}
if (TALER_EC_NONE != ec)
emsg = TALER_ErrorCode_get_hint (ec);
@@ -2575,6 +2456,7 @@ action_challenge_paying (void)
GNUNET_JSON_spec_end ()
};
GdkPixbuf *pb;
+ GtkWidget *w;
if (GNUNET_OK !=
GNUNET_JSON_parse (ppr,
@@ -2593,9 +2475,10 @@ action_challenge_paying (void)
"payment"))
continue;
found = true;
- pb = setup_qrcode ("unpaid_qr_treeview",
- payto,
- strlen (payto));
+ w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview"));
+ pb = AG_setup_qrcode (w,
+ payto,
+ strlen (payto));
if (NULL == pb)
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_ ("Failed to initialize QR-code pixbuf for `%s'\n"),
@@ -3179,22 +3062,22 @@ action_recovery_finished (void)
AG_show ("anastasis_gtk_secret_copy_button");
}
pb = NULL;
+ img = GTK_IMAGE (GCG_get_main_window_object (
+ "anastasis_gtk_secret_qr_image"));
if (NULL != text)
{
- pb = setup_qrcode ("anastasis_gtk_secret_qr_image",
- text,
- strlen (text));
+ pb = AG_setup_qrcode (GTK_WIDGET (img),
+ text,
+ strlen (text));
}
else
{
- pb = setup_qrcode ("anastasis_gtk_secret_qr_image",
- data,
- data_size);
+ pb = AG_setup_qrcode (GTK_WIDGET (img),
+ data,
+ data_size);
}
if (NULL != pb)
{
- img = GTK_IMAGE (GCG_get_main_window_object (
- "anastasis_gtk_secret_qr_image"));
gtk_image_set_from_pixbuf (img,
pb);
g_object_unref (pb);
diff --git a/src/anastasis/anastasis-gtk_handle-method-totp.c b/src/anastasis/anastasis-gtk_handle-method-totp.c
new file mode 100644
index 0000000..c4faa5c
--- /dev/null
+++ b/src/anastasis/anastasis-gtk_handle-method-totp.c
@@ -0,0 +1,235 @@
+/*
+ This file is part of anastasis-gtk.
+ Copyright (C) 2021 Anastasis SARL
+
+ Anastasis is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ Anastasis is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Anastasis; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file src/anastasis/anastasis-gtk_handle-method-totp.c
+ * @brief Handle dialogs for TOTP (RFC 6238)
+ * @author Christian Grothoff
+ */
+#include <gnunet/platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "anastasis-gtk_action.h"
+#include "anastasis-gtk_helper.h"
+#include "anastasis-gtk_handle-identity-changed.h"
+#include <jansson.h>
+
+
+/**
+ * Random secret used in the current dialog.
+ */
+static char totp[32];
+
+
+/**
+ * Compute RFC 4648 base32 encoding of @a val and write
+ * result to @a enc.
+ *
+ * @param val value to encode
+ * @param val_size number of bytes in @a val
+ * @param[out] enc where to write the 0-terminated result
+ */
+static void
+base32enc (const void *val,
+ size_t val_size,
+ char *enc)
+{
+ /**
+ * 32 characters for encoding, using RFC 4648.
+ */
+ static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTV";
+ unsigned int wpos;
+ unsigned int rpos;
+ unsigned int bits;
+ unsigned int vbit;
+ const unsigned char *udata;
+
+ udata = val;
+ vbit = 0;
+ wpos = 0;
+ rpos = 0;
+ bits = 0;
+ while ((rpos < val_size) || (vbit > 0))
+ {
+ if ((rpos < val_size) && (vbit < 5))
+ {
+ bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */
+ vbit += 8;
+ }
+ if (vbit < 5)
+ {
+ bits <<= (5 - vbit); /* zero-padding */
+ GNUNET_assert (vbit == ((val_size * 8) % 5));
+ vbit = 5;
+ }
+ enc[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
+ vbit -= 5;
+ }
+ GNUNET_assert (0 == vbit);
+ if (wpos < val_size)
+ enc[wpos] = '\0';
+}
+
+
+/**
+ * Recompute the QR code shown in @a builder from
+ * totp and the user's name for the secret.
+ *
+ * @param builder the dialog builder
+ */
+static void
+refresh_totp (GtkBuilder *builder)
+{
+ GtkEntry *q;
+ const char *name;
+ char *u_name;
+ char *uri;
+ char base_sec[sizeof (totp) * 2];
+ GdkPixbuf *pb;
+ GtkImage *img;
+
+ gtk_widget_set_sensitive (
+ GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_totp_dialog_btn_ok")),
+ 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),
+ base_sec);
+ GNUNET_asprintf (&uri,
+ "otpauth://totp/%s?secret=%s",
+ u_name,
+ base_sec);
+ GNUNET_free (u_name);
+ img = GTK_IMAGE (gtk_builder_get_object (builder,
+ "qr_image"));
+ pb = AG_setup_qrcode (GTK_WIDGET (img),
+ uri,
+ strlen (uri));
+ if (NULL != pb)
+ {
+ 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);
+ }
+}
+
+
+/**
+ * Function called from the totp dialog upon completion.
+ *
+ * @param dialog the pseudonym selection dialog
+ * @param response_id response code from the dialog
+ * @param user_data the builder of the dialog
+ */
+void
+anastasis_gtk_b_totp_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+ GtkEntry *q;
+ const char *name;
+ json_t *args;
+
+ if (GTK_RESPONSE_OK != response_id)
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ return;
+ }
+ q = GTK_ENTRY (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_totp_dialog_name_entry"));
+ name = gtk_entry_get_text (q);
+ args = json_pack ("{ s:{s:s, s:o, s:s}}",
+ "authentication_method",
+ "type",
+ "totp",
+ "challenge",
+ GNUNET_JSON_from_data (totp,
+ sizeof (totp)),
+ "instructions",
+ name);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_object_unref (G_OBJECT (builder));
+ memset (totp,
+ 0,
+ sizeof (totp));
+ AG_freeze ();
+ AG_ra = ANASTASIS_redux_action (AG_redux_state,
+ "add_authentication",
+ args,
+ &AG_action_cb,
+ NULL);
+ json_decref (args);
+}
+
+
+void
+anastasis_gtk_b_totp_dialog_name_entry_changed_cb (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkBuilder *builder = GTK_BUILDER (user_data);
+
+ refresh_totp (builder);
+}
+
+
+/**
+ * Callback invoked if the the "totp"-button is clicked.
+ *
+ * @param object
+ * @param user_data unused
+ */
+void
+anastasis_gtk_btn_add_auth_totp_clicked_cb (GObject *object,
+ gpointer user_data)
+{
+ GtkWidget *ad;
+ GtkBuilder *builder;
+
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ totp,
+ sizeof (totp));
+ builder = GNUNET_GTK_get_new_builder ("anastasis_gtk_auth_add_totp.glade",
+ NULL);
+ if (NULL == builder)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ad = GTK_WIDGET (gtk_builder_get_object (builder,
+ "anastasis_gtk_b_totp_dialog"));
+ refresh_totp (builder);
+ {
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object));
+ gtk_window_set_transient_for (GTK_WINDOW (ad),
+ GTK_WINDOW (toplevel));
+ gtk_window_present (GTK_WINDOW (ad));
+ }
+}
diff --git a/src/anastasis/anastasis-gtk_helper.c b/src/anastasis/anastasis-gtk_helper.c
index 84b369d..755561f 100644
--- a/src/anastasis/anastasis-gtk_helper.c
+++ b/src/anastasis/anastasis-gtk_helper.c
@@ -28,6 +28,8 @@
#include <gnunet/gnunet_util_lib.h>
#include "anastasis-gtk_helper.h"
#include <jansson.h>
+#include <qrencode.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
/**
@@ -263,3 +265,134 @@ AG_error (const char *format,
AG_have_error = true;
gtk_widget_show (GTK_WIDGET (l));
}
+
+
+/**
+ * Create a the QR code image from a given @a text.
+ *
+ * @param scale factor for scaling up the size of the image to create
+ * @param text text to encode
+ * @return NULL on error
+ */
+static GdkPixbuf *
+create_qrcode (unsigned int scale,
+ const char *text,
+ size_t text_size)
+{
+ QRinput *qri;
+ QRcode *qrc;
+ GdkPixbuf *pb;
+ guchar *pixels;
+ int n_channels;
+ const char *dir;
+ char *fn;
+ unsigned int size;
+
+ qri = QRinput_new2 (0, QR_ECLEVEL_M);
+ if (NULL == qri)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2");
+ return NULL;
+ }
+ /* first try encoding as uppercase-only alpha-numerical
+ QR code (much smaller encoding); if that fails, also
+ try using binary encoding (in case nick contains
+ special characters). */
+ if ((0 != QRinput_append (qri,
+ QR_MODE_AN,
+ text_size,
+ (unsigned char *) text)) &&
+ (0 != QRinput_append (qri,
+ QR_MODE_8,
+ text_size,
+ (unsigned char *) text)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRinput_append");
+ return NULL;
+ }
+ qrc = QRcode_encodeInput (qri);
+ if (NULL == qrc)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "QRcode_encodeInput");
+ QRinput_free (qri);
+ return NULL;
+ }
+ /* We use a trick to create a pixbuf in a way that works for both Gtk2 and
+ Gtk3 by loading a dummy file from disk; all other methods are not portable
+ to both Gtk2 and Gtk3. */
+ dir = GNUNET_GTK_get_data_dir ();
+ GNUNET_asprintf (&fn,
+ "%s%s",
+ dir,
+ "qr_dummy.png");
+ size = (qrc->width + 8) * scale;
+ size += 8 - (size % 8);
+ pb = gdk_pixbuf_new_from_file_at_size (fn,
+ size,
+ size,
+ NULL);
+ GNUNET_free (fn);
+ if (NULL == pb)
+ {
+ QRcode_free (qrc);
+ QRinput_free (qri);
+ return NULL;
+ }
+ pixels = gdk_pixbuf_get_pixels (pb);
+ n_channels = gdk_pixbuf_get_n_channels (pb);
+ for (unsigned int x = 4 * scale; x < size - 4 * scale; x++)
+ for (unsigned int y = 4 * scale; y < size - 4 * scale; y++)
+ {
+ unsigned int xx = x - 4 * scale;
+ unsigned int yy = y - 4 * scale;
+ unsigned int ss = size - 8 * scale;
+ unsigned int off =
+ (xx * qrc->width / ss) + (yy * qrc->width / ss) * qrc->width;
+ for (int c = 0; c < n_channels; c++)
+ pixels[(y * size + x) * n_channels + c] =
+ (0 == (qrc->data[off] & 1)) ? 0xFF : 0;
+ }
+ QRcode_free (qrc);
+ QRinput_free (qri);
+ return pb;
+}
+
+
+GdkPixbuf *
+AG_setup_qrcode (GtkWidget *w,
+ const char *text,
+ size_t text_size)
+{
+ GtkWidget *image;
+ GdkScreen *screen;
+ GtkSettings *settings;
+ gint dpi;
+ int scale;
+ GdkPixbuf *pb;
+
+ if (NULL == w)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ /* adjust scale to screen resolution */
+ screen = gtk_widget_get_screen (w);
+ settings = gtk_settings_get_for_screen (screen);
+ g_object_get (G_OBJECT (settings),
+ "gtk-xft-dpi",
+ &dpi,
+ NULL);
+ if (-1 == dpi)
+ scale = 2;
+ else if (dpi >= 122800)
+ scale = 4;
+ else if (dpi >= 98304)
+ scale = 3;
+ else
+ scale = 2;
+ return create_qrcode (3 * scale,
+ text,
+ text_size);
+}
diff --git a/src/anastasis/anastasis-gtk_helper.h b/src/anastasis/anastasis-gtk_helper.h
index 28ddc74..f76f71f 100644
--- a/src/anastasis/anastasis-gtk_helper.h
+++ b/src/anastasis/anastasis-gtk_helper.h
@@ -31,6 +31,7 @@
#include <anastasis/anastasis_service.h>
#include <anastasis/anastasis_redux.h>
#include "anastasis-gtk.h"
+#include <gdk-pixbuf/gdk-pixbuf.h>
/**
@@ -476,4 +477,18 @@ void
AG_error_clear (void);
+/**
+ * Setup QR code image for a widget @a w.
+ *
+ * @param w widget to use to determine screen resolution
+ * @param text text to encode
+ * @param text_size number of bytes in @a text
+ * @return NULL on failure
+ */
+GdkPixbuf *
+AG_setup_qrcode (GtkWidget *w,
+ const char *text,
+ size_t text_size);
+
+
#endif