kee

Offline IOU signer with QR as transport
git clone https://holbrook.no/src/kee
Info | Log | Files | Refs | README | LICENSE

commit 85dce1095234a95772f83707ecd76713d3c2fc7d
parent 1448fc7d98d3e88fc6e20c4086b7ecf3020d140b
Author: lash <dev@holbrook.no>
Date:   Sat, 27 Apr 2024 13:48:50 +0100

Implement qr encoder and qr display widget

Diffstat:
MREADME.md | 2++
Msrc/Makefile | 6++----
Msrc/err.h | 5+++--
Msrc/gtk/Makefile | 7++-----
Msrc/gtk/kee-entry.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Asrc/gtk/kee-transport.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gtk/kee-transport.h | 24++++++++++++++++++++++++
Msrc/gtk/ui.c | 15+++++++++++++--
Asrc/qr.c | 25+++++++++++++++++++++++++
Asrc/qr.h | 19+++++++++++++++++++
Msrc/tests/Makefile | 5++---
Msrc/transport.c | 6++++++
Msrc/transport.h | 4++--
13 files changed, 261 insertions(+), 24 deletions(-)

diff --git a/README.md b/README.md @@ -12,11 +12,13 @@ Below are the library versions of the [archlinux](https://archlinux.org/) compon | package | version | |---|---| | gcc | 13.2.1 | +| graphicsmagick | 1.3.42 | | gst-plugins-bad | 1.24.0 | | gstreamer | 1.24.0 | | gtk4 | 4.12.5 | | libb64 | 1.2.1 | | libgcrypt | 1.10.3 | +| qrencode | 4.1.1 | | libxdg-basedir | 1.2.3 | | lmdb | 0.9.32 | | openldap | 2.6.7 | diff --git a/src/Makefile b/src/Makefile @@ -1,9 +1,7 @@ OBJS := $(patsubst %.c,%.o,$(filter-out main.c,$(wildcard *.c))) -INCLUDES := `pkg-config --cflags libgcrypt lmdb libxdg-basedir` -#CFLAGS += $(INCLUDES) -g3 -Wall -Iaux/varint +INCLUDES := `pkg-config --cflags libgcrypt lmdb libxdg-basedir libqrencode zbar` CFLAGS += $(INCLUDES) -Wall -#LIBS := `pkg-config --libs libgcrypt zlib lmdb libxdg-basedir` -lb64 -lvarint -llash -LIBS := `pkg-config --libs libgcrypt zlib lmdb libxdg-basedir` -lb64 -llash +LIBS := `pkg-config --libs libgcrypt zlib lmdb libxdg-basedir libqrencode zbar` -lb64 -llash LDFLAGS += $(LIBS) #all: aux resource $(OBJS) diff --git a/src/err.h b/src/err.h @@ -27,10 +27,11 @@ enum keeError { ERR_NOKEY, /// Crypto authentication fail ERR_KEYFAIL, - /// ERR_ALREADY_SIGNED, - // ERR_INVALID_CMD, + ERR_QR_MISSING, + ERR_QR_INVALID, + ERR_SPACE, }; #endif // _KEE_ERR_H diff --git a/src/gtk/Makefile b/src/gtk/Makefile @@ -1,11 +1,8 @@ OBJS := $(patsubst %.c,%.o,$(filter-out main.c,$(wildcard *.c))) LINKOBJS := $(wildcard ../*.o) $(OBJS) INCLUDES := -I.. -I../aux/include -#CFLAGS += `pkg-config --cflags gtk4 gstreamer-1.0 libcmime` $(INCLUDES) -g3 -Wall -CFLAGS += `pkg-config --cflags gtk4 gstreamer-1.0 libtasn1` $(INCLUDES) -g3 -Wall -#LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0 libcmime` -lb64 -#LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0 libtasn1` -lb64 -lcmime -lvarint -llash -LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0 libtasn1` -lb64 -lcmime -llash -lldap +CFLAGS += `pkg-config --cflags gtk4 gstreamer-1.0 libtasn1 libqrencode zbar` $(INCLUDES) -g3 -Wall +LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0 libtasn1 libqrencode zbar` -lb64 -lcmime -llash -lldap LDFLAGS += $(LIBS) all: resource $(OBJS) diff --git a/src/gtk/kee-entry.c b/src/gtk/kee-entry.c @@ -12,6 +12,7 @@ #include "kee-entry.h" #include "kee-entry-item.h" #include "kee-entry-item-store.h" +#include "kee-transport.h" #include "db.h" #include "err.h" #include "hex.h" @@ -24,6 +25,8 @@ #include "ledger.h" #include "dn.h" #include "gpg.h" +#include "transport.h" +#include "qr.h" typedef struct { } KeeEntryPrivate; @@ -70,10 +73,19 @@ struct _KeeEntry { G_DEFINE_TYPE(KeeEntry, kee_entry, GTK_TYPE_BOX); static void kee_entry_handle_add(GtkButton *butt, KeeEntry *o) { + int r; + GtkWindow *win; + GtkWidget *widget; + GtkApplication *gapp; + GAction *act; struct kee_ledger_item_t *item; GtkEntryBuffer *buf; char *b; size_t c; + struct kee_transport_t trans; + char *out; + size_t out_len; + GVariant *transport_data; buf = gtk_entry_get_buffer(o->form->uoa); b = (char*)gtk_entry_buffer_get_text(buf); @@ -97,16 +109,53 @@ static void kee_entry_handle_add(GtkButton *butt, KeeEntry *o) { buf = gtk_entry_get_buffer(o->form->bob_pubkey); b = (char*)gtk_entry_buffer_get_text(buf); c = hex2bin(b, (unsigned char*)o->ledger.pubkey_bob); - if (c == 0) { - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "invalid counterparty public key data"); +// if (c == 0) { +// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "invalid counterparty public key data"); +// return; +// } else if (c != PUBKEY_LENGTH) { +// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "wrong size for counterparty public key"); +// return; +// } + + o->state |= ENTRYSTATE_LOAD; + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "adding ledger entry"); + + out_len = 1024; + out = malloc(out_len); + if (out == NULL) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "memory for item serialization buffer for qr transport fail"); return; - } else if (c != PUBKEY_LENGTH) { - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "wrong size for counterparty public key"); + } + r = kee_ledger_item_serialize(item, out, &out_len, KEE_LEDGER_ITEM_SERIALIZE_REQUEST); + if (r) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "item serialization failed"); return; } - o->state |= ENTRYSTATE_LOAD; - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "adding ledger entry"); + r = kee_transport_single(&trans, KEE_TRANSPORT_BASE64, KEE_CMD_LEDGER, c); + if (r) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "qr transport renderer failed"); + return; + } + + r = kee_transport_write(&trans, out, out_len); + if (r) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "write to qr transport renderer failed"); + return; + } + + r = kee_transport_next(&trans, out, &out_len); + if (r) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "read from qr transport renderer failed"); + return; + } + transport_data = g_variant_new_take_string(out); + + widget = gtk_widget_get_ancestor(GTK_WIDGET(o), GTK_TYPE_WINDOW); + win = GTK_WINDOW(widget); + gapp = gtk_window_get_application(win); + act = g_action_map_lookup_action(G_ACTION_MAP(gapp), "qr"); + g_action_activate(act, transport_data); } static void kee_entry_handle_item_setup(GtkListItemFactory* o, GtkListItem *item) { diff --git a/src/gtk/kee-transport.c b/src/gtk/kee-transport.c @@ -0,0 +1,106 @@ +#include <glib-object.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "kee-transport.h" +#include "kee-menu.h" +#include "err.h" +#include "qr.h" + + +typedef struct { +} KeeTransportPrivate; + +struct _KeeTransportClass { + GtkWidget parent_class; +}; + +struct _KeeTransport { + GtkWidget parent; + char *image_data; + size_t image_width; + size_t image_size; + GdkPixbuf *pixbuf; +}; + +G_DEFINE_TYPE(KeeTransport, kee_transport, GTK_TYPE_BOX); + +static void kee_transport_finalize(GObject *o) { + KeeTransport *trans = KEE_TRANSPORT(o); + free(trans->image_data); +} + +static void kee_transport_class_init(KeeTransportClass *kls) { + GObjectClass *object_class = G_OBJECT_CLASS(kls); + object_class->finalize = kee_transport_finalize; +} + +static void kee_transport_init(KeeTransport *o) { + o->image_data = malloc(QR_IMAGE_BYTES); +} + +/// \todo find a way to modify underlying bytes and keep the stack from pixbuf to widget +static void kee_transport_render(KeeTransport *o) { + KeeMenu *menu; + GtkWidget *widget; + GdkTexture *texture; + GdkPixbuf *pixbuf; + GBytes *bytes; + size_t width_bytes; + + width_bytes = o->image_width * QR_IMAGE_COMPONENTS; + + bytes = g_bytes_new(o->image_data, o->image_size); + pixbuf = gdk_pixbuf_new_from_bytes(bytes, GDK_COLORSPACE_RGB, false, QR_IMAGE_BIT_DEPTH, o->image_width, o->image_width, width_bytes); + texture = gdk_texture_new_for_pixbuf(pixbuf); + widget = gtk_widget_get_first_child(GTK_WIDGET(o)); + if (widget) { + gtk_box_remove(GTK_BOX(o), widget); + } + widget = gtk_picture_new_for_paintable(GDK_PAINTABLE(texture)); + gtk_picture_set_content_fit(GTK_PICTURE(widget), GTK_CONTENT_FIT_SCALE_DOWN); + gtk_box_append(GTK_BOX(o), widget); + + widget = gtk_widget_get_ancestor(GTK_WIDGET(o), KEE_TYPE_MENU); + menu = KEE_MENU(widget); + kee_menu_next(menu, "transport"); +} + +/// \todo share buffer with image data? +void kee_transport_handle_qr(GAction *Act, GVariant *v, KeeTransport *o) { + size_t c; + char *p; + int i; + char r; + char *b; + char out[QR_CAP * QR_CAP]; + + b = (char*)g_variant_get_string(v, NULL); + if (b == NULL) { + return; + } + + c = QR_CAP * QR_CAP; + r = (char)qr_encode(b, out, &o->image_width); + if (r) { + return; + } + + p = o->image_data; + for (i = 0; i < (int)(o->image_width * o->image_width); i++) { + if (*(out+i) & 0x01) { + r = 0x00; + } else { + r = 0xff; + } + *p = r; + p++; + *p = r; + p++; + *p = r; + p++; + } + o->image_size = (o->image_width * QR_IMAGE_COMPONENTS) * (o->image_width * QR_IMAGE_COMPONENTS); + kee_transport_render(o); +} diff --git a/src/gtk/kee-transport.h b/src/gtk/kee-transport.h @@ -0,0 +1,24 @@ +#ifndef _GTK_KEE_TRANSPORT_H +#define _GTK_KEE_TRANSPORT_H + +#include "qr.h" + +#define QR_IMAGE_COMPONENTS 3 +#define QR_IMAGE_BIT_DEPTH 8 +#define QR_IMAGE_WIDTH QR_CAP * QR_MODULE_SIZE +#define QR_IMAGE_SIZE QR_IMAGE_WIDTH * QR_IMAGE_WIDTH +#define QR_IMAGE_BYTES QR_IMAGE_SIZE * QR_IMAGE_COMPONENTS + +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define KEE_TYPE_TRANSPORT kee_transport_get_type() +G_DECLARE_FINAL_TYPE(KeeTransport, kee_transport, KEE, TRANSPORT, GtkBox); + +void kee_transport_handle_qr(GAction *Act, GVariant *v, KeeTransport *o); + +G_END_DECLS + +#endif //_GTK_KEE_TRANSPORT_H diff --git a/src/gtk/ui.c b/src/gtk/ui.c @@ -16,6 +16,7 @@ #include "kee-entry-store.h" #include "kee-menu.h" #include "kee-key.h" +#include "kee-transport.h" static void ui_handle_unlock(KeeKey *o, KeeMenu *menu) { @@ -48,12 +49,14 @@ static void ui_handle_unlock(KeeKey *o, KeeMenu *menu) { //} -void ui_build(GtkApplication *app, struct kee_context *ctx) { +void ui_build(GtkApplication *gapp, struct kee_context *ctx) { + KeeTransport *trans; + GSimpleAction *act; GtkWidget *widget; KeeMenu *win; KeeImport *import; - win = kee_menu_new(app, ctx); + win = kee_menu_new(gapp, ctx); widget = GTK_WIDGET(kee_key_new(&ctx->gpg)); kee_menu_add(win, "unlock", widget); @@ -71,6 +74,14 @@ void ui_build(GtkApplication *app, struct kee_context *ctx) { widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); kee_menu_add(win, "entry", widget); + trans = g_object_new(KEE_TYPE_TRANSPORT, "orientation", GTK_ORIENTATION_VERTICAL, NULL); + kee_menu_add(win, "transport", GTK_WIDGET(trans)); + + /// \todo make kee-entry action map/group + act = g_simple_action_new("qr", G_VARIANT_TYPE_STRING); + g_action_map_add_action(G_ACTION_MAP(gapp), G_ACTION(act)); + g_signal_connect(act, "activate", G_CALLBACK(kee_transport_handle_qr), trans); + gtk_window_present(GTK_WINDOW (win)); } diff --git a/src/qr.c b/src/qr.c @@ -0,0 +1,25 @@ +#include <string.h> +#include <stddef.h> +#include <qrencode.h> + +#include "qr.h" +#include "err.h" + + +int qr_encode(char *in, char *out, size_t *out_len) { + QRcode *qr; + + //qr = QRcode_encodeString8bit((const char*)in, QRSPEC_VERSION_MAX, QR_ECLEVEL_M); + qr = QRcode_encodeString8bit((const char*)in, QR_VERSION, QR_ECLEVEL_L); + if (qr == NULL) { + return 1; + } + //qr_render((char*)qr->data, qr->width, out, out_len); + memcpy(out, (char*)qr->data, qr->width * qr->width); + + *out_len = qr->width; + + QRcode_free(qr); + + return 0; +} diff --git a/src/qr.h b/src/qr.h @@ -0,0 +1,19 @@ +#ifndef KEE_QR_H_ +#define KEE_QR_H_ + +#include <stddef.h> + +#ifndef QR_MODULE_SIZE +#define QR_MODULE_SIZE 7 +#endif + +#ifndef QR_CAP +#define QR_CAP 125 // ~1024 bytes +#endif + +#define QR_VERSION 20 + + +int qr_encode(char *in, char *out, size_t *out_len); + +#endif // KEE_QR_H_ diff --git a/src/tests/Makefile b/src/tests/Makefile @@ -3,9 +3,8 @@ OBJS := $(patsubst %.c,%.o,$(wildcard *.c)) LINKOBJS := $(wildcard ../*.o) INCLUDES := -I.. -CFLAGS += `pkg-config --cflags gtk4 gstreamer-1.0` $(INCLUDES) -Wall -#LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0` -lb64 -lvarint -llash -LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0` -lb64 -llash -ltasn1 -lcmime -lldap +CFLAGS += `pkg-config --cflags gtk4 gstreamer-1.0 zbar` $(INCLUDES) -Wall +LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0 libqrencode zbar` -lb64 -llash -ltasn1 -lcmime -lldap LDFLAGS += $(LIBS) all: obj_debug $(OBJS) diff --git a/src/transport.c b/src/transport.c @@ -163,6 +163,12 @@ int kee_transport_single(struct kee_transport_t *trans, enum kee_transport_mode_ return ERR_OK; } +void kee_transport_set_response(struct kee_transport_t *trans) { + if (trans->state == 0) { + *trans->cmd |= KEE_CMD_SIGN_RESPONSE; + } +} + int kee_transport_write(struct kee_transport_t *trans, const char *in, size_t in_len) { if (trans->state) { return ERR_FAIL; diff --git a/src/transport.h b/src/transport.h @@ -18,8 +18,7 @@ enum kee_cmd_e { // max number 31 KEE_N_CMD, }; -#define KEE_CMD_SIGN_RESPONSE 32 -#define KEE_CMD_SIGN_REQUEST 64 +#define KEE_CMD_SIGN_RESPONSE 64 #define KEE_CMD_CHUNKED 128 struct kee_transport_t { @@ -59,5 +58,6 @@ int unpack(char *in, size_t in_len, char *out, size_t *out_len); int kee_transport_single(struct kee_transport_t *trans, enum kee_transport_mode_e mode, char cmd, size_t data_len); int kee_transport_write(struct kee_transport_t *trans, const char *in, size_t in_len); int kee_transport_next(struct kee_transport_t *trans, char *out, size_t *out_len); +void kee_transport_set_response(struct kee_transport_t *trans); #endif // _KEE_TRANSPORT_H