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:
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