kee

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

commit e6b79cc1e100fb2029d40041bbdf6a97b45afc90
Author: lash <dev@holbrook.no>
Date:   Wed, 28 Feb 2024 02:46:59 +0000

Initial commit

Diffstat:
A.gitignore | 3+++
AMakefile | 17+++++++++++++++++
Aglade.ui | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Makefile | 19+++++++++++++++++++
Asrc/aux/Makefile | 2++
Asrc/aux/README.md | 5+++++
Asrc/aux/varint/.package | 2++
Asrc/aux/varint/CMakeLists.txt | 4++++
Asrc/aux/varint/Makefile | 2++
Asrc/aux/varint/README.md | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/aux/varint/varint.c | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/aux/varint/varint.h | 15+++++++++++++++
Asrc/aux/varint/varint.o | 0
Asrc/command.h | 7+++++++
Asrc/common.h | 6++++++
Asrc/context.h | 8++++++++
Asrc/db.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/db.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/debug.c | 20++++++++++++++++++++
Asrc/debug.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/digest.c | 27+++++++++++++++++++++++++++
Asrc/digest.h | 23+++++++++++++++++++++++
Asrc/endian.c | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/endian.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/err.h | 19+++++++++++++++++++
Asrc/export.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/export.h | 36++++++++++++++++++++++++++++++++++++
Asrc/gpg.h | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/hex.c | 21+++++++++++++++++++++
Asrc/hex.h | 22++++++++++++++++++++++
Asrc/item.h | 6++++++
Asrc/kee.gresource.xml | 6++++++
Asrc/main.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/resources.c | 324+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rpc.h | 19+++++++++++++++++++
Asrc/settings.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/settings.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/term_debug.c | 7+++++++
Asrc/transport.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/transport.h | 40++++++++++++++++++++++++++++++++++++++++
Asrc/ui.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ui.h | 16++++++++++++++++
Atestdata.py | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
43 files changed, 2111 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +src/*.ui +testdata_mdb +*\~ diff --git a/Makefile b/Makefile @@ -0,0 +1,17 @@ +all: subs + +subs: glade + make -C src + +glade: + gtk4-builder-tool simplify --3to4 glade.ui > src/main.ui + +clean: + rm -vf src/*.o + rm -vf src/a.out + +run: all + G_MESSAGES_DEBUG=all ./src/a.out + +debug: all + G_DEBUG=3 G_MESSAGES_DEBUG=all ./src/a.out diff --git a/glade.ui b/glade.ui @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.40.0 --> +<interface> + <requires lib="gtk+" version="3.24"/> + <object class="GtkWindow" id="keechoose"> + <property name="can-focus">False</property> + <child> + <object class="GtkListBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + </object> + </child> + </object> + <object class="GtkWindow" id="keescan"> + <property name="can-focus">False</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">gtk-missing-image</property> + <property name="use-fallback">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + <object class="GtkWindow" id="keeunlock"> + <property name="can-focus">False</property> + <child> + <object class="GtkBox" id="keeunlockbox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkEntry" id="keeunlockpassphrase"> + <property name="visible">True</property> + <property name="can-focus">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="keeunlocksubmit"> + <property name="label" translatable="yes">button</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + <object class="GtkWindow" id="keeview"> + <property name="can-focus">False</property> + <child> + <!-- n-columns=3 n-rows=3 --> + <object class="GtkGrid" id="view"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="use-fallback">True</property> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> + <property name="width">2</property> + <property name="height">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> +</interface> diff --git a/src/Makefile b/src/Makefile @@ -0,0 +1,19 @@ +OBJS := $(patsubst %.c,%.o,$(filter-out main.c,$(wildcard *.c))) +INCLUDES := `pkg-config --cflags gtk4 libgcrypt lmdb libxdg-basedir` +CFLAGS += $(INCLUDES) -g3 -Wall -Iaux/varint +LIBS := `pkg-config --libs gtk4 libgcrypt zlib lmdb libxdg-basedir` -lb64 +LDFLAGS += $(LIBS) + +all: aux resource $(OBJS) + $(CC) $(CFLAGS) main.c -o a.out $(OBJS) $(LDFLAGS) aux/varint/varint.o + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS) + +resource: + glib-compile-resources kee.gresource.xml --target=resources.c --generate-source + +aux: + make -C aux + +.PHONY: clean aux diff --git a/src/aux/Makefile b/src/aux/Makefile @@ -0,0 +1,2 @@ +all: + make -C varint diff --git a/src/aux/README.md b/src/aux/README.md @@ -0,0 +1,5 @@ +add auxiliary source files here. + +e.g. implementation of varint + +TODO: varint header file diff --git a/src/aux/varint/.package b/src/aux/varint/.package @@ -0,0 +1,2 @@ +file varint.h +file varint.c diff --git a/src/aux/varint/CMakeLists.txt b/src/aux/varint/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(varint varint.c) +set_property(TARGET varint PROPERTY C_VISIBILITY_PRESET default) +set_property(TARGET varint PROPERTY VISIBILITY_INLINES_HIDDEN OFF) +include_directories(.) diff --git a/src/aux/varint/Makefile b/src/aux/varint/Makefile @@ -0,0 +1,2 @@ +all: + $(CC) -c varint.c -o varint.o diff --git a/src/aux/varint/README.md b/src/aux/varint/README.md @@ -0,0 +1,59 @@ +# varint.c + +Read and write variable sized integers in C. +Compatible with Protobuf and Go varints. + +## API +```c +// read uint64 or int64 from data. returns the number of bytes read or returns +// the zero if there is not enough data or returns -1 if the data does not +// represent a valid varint. +int varint_read_u(const void *data, size_t len, uint64_t *x); +int varint_read_i(const void *data, size_t len, int64_t *x); + +// write a uint64 or int64 to data buffer. The data buffer must be at least +// 10 bytes in order to hold the maximum sized varint. Returns the number of +// bytes written. +int varint_write_u(void *data, uint64_t x); +int varint_write_i(void *data, int64_t x); +``` + +## Example + +```c +#include <stdio.h> +#include "varint.h" + +int main() { + char buf[100]; + int n; + int64_t i; + uint64_t u; + + // write and read a signed integer + i = -305; + n = varint_write_i(buf, i); + printf("wrote the value %lld (%d bytes)\n", (long long)i, n); + n = varint_read_i(buf, sizeof(buf), &i); + printf("read the value %lld (%d bytes)\n", (long long)i, n); + + + // write and read an unsigned integer + u = 102993; + n = varint_write_u(buf, u); + printf("wrote the value %llu (%d bytes)\n", (unsigned long long)u, n); + n = varint_read_u(buf, sizeof(buf), &u); + printf("read the value %lld (%d bytes)\n", (unsigned long long)u, n); +} +// output: +// wrote the value -305 (2 bytes) +// read the value -305 (2 bytes) +// wrote the value 102993 (3 bytes) +// read the value 102993 (3 bytes) +``` + +## Running tests + +```sh +$ cc -DVARINT_TEST varint.c && ./a.out +``` diff --git a/src/aux/varint/varint.c b/src/aux/varint/varint.c @@ -0,0 +1,168 @@ +// Copyright 2020 Joshua J Baker. All rights reserved. + +#include "varint.h" + +// varint_write_u writes a uint64 varint to data, which could be to 10 bytes. +// Make sure that you provide a data buffer that can take 10 bytes! +// Returns the number of bytes written. +int varint_write_u(void *data, uint64_t x) { + char *str = data; + uint64_t n = 0; + n+=x>=0x80; str[0]=x|0x80; x>>=7; + n+=x>=0x80; str[1]=x|0x80; x>>=7; + n+=x>=0x80; str[2]=x|0x80; x>>=7; + n+=x>=0x80; str[3]=x|0x80; x>>=7; + n+=x>=0x80; str[4]=x|0x80; x>>=7; + n+=x>=0x80; str[5]=x|0x80; x>>=7; + n+=x>=0x80; str[6]=x|0x80; x>>=7; + n+=x>=0x80; str[7]=x|0x80; x>>=7; + n+=x>=0x80; str[8]=x|0x80; x>>=7; + n+=x>=0x80; str[9]=x|0x80; x>>=7; + str[n] ^= 0x80; + return n+1; +} + +// varint_write_i writes a int64 varint to data, which could be to 10 bytes. +// Make sure that you provide a data buffer that can take 10 bytes! +// Returns the number of bytes written. +int varint_write_i(void *buf, int64_t x) { + uint64_t ux = (uint64_t)x << 1; + if (x < 0) { + ux = ~ux; + } + return varint_write_u(buf, ux); +} + +// varint_read_u reads a uint64 varint from data. +// Returns the number of bytes reads, or returns 0 if there's not enough data +// to complete the read, or returns -1 if the data buffer does not represent +// a valid uint64 varint. +int varint_read_u(const void *data, size_t len, uint64_t *x) { + const char *str = data; + uint64_t b; + *x = 0; + if (len==0) return 0; b=str[0]; *x|=(b&0x7f)<<(7*0); if (b<0x80) return 0+1; + if (len==1) return 0; b=str[1]; *x|=(b&0x7f)<<(7*1); if (b<0x80) return 1+1; + if (len==2) return 0; b=str[2]; *x|=(b&0x7f)<<(7*2); if (b<0x80) return 2+1; + if (len==3) return 0; b=str[3]; *x|=(b&0x7f)<<(7*3); if (b<0x80) return 3+1; + if (len==4) return 0; b=str[4]; *x|=(b&0x7f)<<(7*4); if (b<0x80) return 4+1; + if (len==5) return 0; b=str[5]; *x|=(b&0x7f)<<(7*5); if (b<0x80) return 5+1; + if (len==6) return 0; b=str[6]; *x|=(b&0x7f)<<(7*6); if (b<0x80) return 6+1; + if (len==7) return 0; b=str[7]; *x|=(b&0x7f)<<(7*7); if (b<0x80) return 7+1; + if (len==8) return 0; b=str[8]; *x|=(b&0x7f)<<(7*8); if (b<0x80) return 8+1; + if (len==9) return 0; b=str[9]; *x|=(b&0x7f)<<(7*9); if (b<0x80) return 9+1; + return -1; +} + +// varint_read_i reads a int64 varint from data. +// Returns the number of bytes reads, or returns 0 if there's not enough data +// to complete the read, or returns -1 if the data buffer does not represent +// a valid int64 varint. +int varint_read_i(const void *data, size_t len, int64_t *x) { + uint64_t ux; + int n = varint_read_u(data, len, &ux); + *x = (int64_t)(ux >> 1); + if ((ux&1) != 0) { + *x = ~*x; + } + return n; +} + +//============================================================================== +// TESTS +// $ cc -DVARINT_TEST buf.c && ./a.out # run tests +//============================================================================== +#ifdef VARINT_TEST +#include <math.h> +#include <time.h> +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +static uint64_t rand_uint() { + return (((uint64_t)rand()<<33) | + ((uint64_t)rand()<<1) | + ((uint64_t)rand()<<0)) >> (rand()&63); +} + +static int64_t rand_int() { + return (int64_t)(rand_uint()) * ((rand()&1)?1:-1); +} + +static void rand_fill(char *data, int len) { + int i = 0; + for (; i < len-3; i += 3) { + uint32_t r = rand(); + data[i+0] = r; + data[i+1] = r >> 8; + data[i+2] = r >> 16; + } + for (; i < len; i++) { + data[i] = rand(); + } +} + +int main() { + printf("Running varint.c tests...\n"); + int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL); + printf("seed=%d\n", seed); + srand(seed); + int N = 500000; + uint64_t *uints = malloc(sizeof(uint64_t)*N); + char *data = malloc(10*N); + assert(uints && data); + printf("[RANDOMS] "); + for (int h = 0; h < 50; h++) { + for (int i = 0, n = 0; i < N; i++) { + uints[i] = rand_uint(); + int nn = varint_write_u(data+n, uints[i]); + assert(nn > 0); + n += nn; + } + for (int i = 0, n = 0; i < N; i++) { + uint64_t x; + int nn = varint_read_u(data+n, 10*N-n, &x); + assert(nn > 0); + assert(x == uints[i]); + n += nn; + } + int64_t *ints = (int64_t*)uints; + for (int i = 0, n = 0; i < N; i++) { + ints[i] = rand_int(); + int nn = varint_write_i(data+n, ints[i]); + assert(nn > 0); + n += nn; + } + for (int i = 0, n = 0; i < N; i++) { + int64_t x; + int nn = varint_read_i(data+n, 10*N-n, &x); + assert(nn > 0); + assert(x == ints[i]); + n += nn; + } + printf("."); + fflush(stdout); + } + printf("\n"); + printf("[FUZZING] "); + int sz = 10*N/2; + for (int h = 0; h < 50; h++) { + rand_fill(data, sz); + for (int i = 0; i < sz; i++) { + int64_t x; + varint_read_i(data+i, sz-i, &x); + } + for (int i = 0; i < sz; i++) { + uint64_t x; + varint_read_u(data+i, sz-i, &x); + } + printf("."); + fflush(stdout); + } + printf("\n"); + free(uints); + free(data); + printf("PASSED\n"); +} +#endif diff --git a/src/aux/varint/varint.h b/src/aux/varint/varint.h @@ -0,0 +1,15 @@ +// Copyright 2020 Joshua J Baker. All rights reserved. + +#ifndef VARINT_H +#define VARINT_H + +#include <stdint.h> +#include <stddef.h> + +int varint_read_u(const void *data, size_t len, uint64_t *x); +int varint_read_i(const void *data, size_t len, int64_t *x); + +int varint_write_u(void *data, uint64_t x); +int varint_write_i(void *data, int64_t x); + +#endif diff --git a/src/aux/varint/varint.o b/src/aux/varint/varint.o Binary files differ. diff --git a/src/command.h b/src/command.h @@ -0,0 +1,7 @@ +#ifndef _KEE_CMD_H +#define _KEE_CMD_H + +int process_rpc_command(void *backend, char *buf, size_t buf_len, bool preview); +int preview_command(char *in, size_t in_len, char *out, size_t *out_len); + +#endif // _KEE_CMD_H diff --git a/src/common.h b/src/common.h @@ -0,0 +1,6 @@ +#ifndef _COMMON_H +#define _COMMON_H + +typedef unsigned char kee_boolean; + +#endif // _EXPORT_H diff --git a/src/context.h b/src/context.h @@ -0,0 +1,8 @@ +#ifndef _KEE_CONTEXT +#define _KEE_CONTEXT + +struct kee_context { + +}; + +#endif // _KEE_CONTEXT diff --git a/src/db.c b/src/db.c @@ -0,0 +1,184 @@ +#include <string.h> +#include <fcntl.h> +#include <lmdb.h> +#include <gcrypt.h> +#include <time.h> +#include "db.h" +#include "err.h" +#include "endian.h" + +int db_connect(struct db_ctx *ctx, char *conn) { + int r; + + db_reset(ctx); + ctx->connstr = conn; + r = mdb_env_create(&ctx->env); + if (r) { + return ERR_FAIL; + } + r = mdb_env_open(ctx->env, ctx->connstr, MDB_NOLOCK, S_IRWXU); + if (r) { + return ERR_FAIL; + } + + return ERR_OK; +} + +/** + * \todo split up and optimize + */ +int db_put(struct db_ctx *ctx, enum DbKey pfx, char *data, size_t data_len) { + int r; + char *buf; + char buf_reverse[33]; + unsigned char *rv; + char kv; + struct timespec ts; + char rts[sizeof(struct timespec)]; + gcry_error_t e; + gcry_md_hd_t h; + MDB_txn *tx; + MDB_dbi dbi; + MDB_val k; + MDB_val v; + + buf = (char*)malloc(1 + 32 + sizeof(struct timespec)); // length should be lookup in sizes array for each key + + r = clock_gettime(CLOCK_REALTIME, &ts); + if (r) { + return ERR_FAIL; + } + memcpy(rts, &ts.tv_sec, sizeof(ts.tv_sec)); + memcpy(rts + sizeof(ts.tv_sec), &ts.tv_nsec, sizeof(ts.tv_nsec)); + to_endian(0, sizeof(ts.tv_sec), rts); + to_endian(0, sizeof(ts.tv_nsec), rts + sizeof(ts.tv_sec)); + + e = gcry_md_open(&h, GCRY_MD_SHA256, 0); + if (e) { + return ERR_DIGESTFAIL; + } + gcry_md_write(h, data, data_len); + rv = gcry_md_read(h, 0); + kv = (char)pfx; + memcpy(buf, &kv, 1); + memcpy(buf + 1, rts, sizeof(struct timespec)); + memcpy(buf + 1 + sizeof(struct timespec), rv, 32); + + r = mdb_txn_begin(ctx->env, NULL, 0, &tx); + if (r) { + return ERR_FAIL; + } + + r = mdb_dbi_open(tx, NULL, MDB_CREATE, &dbi); + if (r) { + return ERR_FAIL; + } + + k.mv_data = buf; + k.mv_size = 1 + 32 + sizeof(struct timespec); + v.mv_data = data; + v.mv_size = data_len; + + r = mdb_put(tx, dbi, &k, &v, 0); + if (r) { + return ERR_FAIL; + } + + // put reverse lookup + buf_reverse[0] = (char)DbKeyReverse; + memcpy(buf_reverse+1, rv, 32); + k.mv_data = buf_reverse; + k.mv_size = 33; + v.mv_data = buf; + v.mv_size = 1 + 32 + sizeof(struct timespec); + gcry_md_close(h); // keep the handle open until here because we use its digest value again for the key + + r = mdb_put(tx, dbi, &k, &v, 0); + if (r) { + return ERR_FAIL; + } + + r = mdb_txn_commit(tx); + if (r) { + return ERR_FAIL; + } + + return ERR_OK; + +} + +/** + * + * \todo change cursor to jump to new search match when current (last) prefix does not match lookup prefix. + * + */ +int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, char **value, size_t *value_len) { + int r; + unsigned char start[DB_KEY_SIZE_LIMIT]; + + memset(start, 0, DB_KEY_SIZE_LIMIT);; + if ((char)pfx == 0) { + return ERR_DB_INVALID; + } + + if (ctx->current_key == DbNoKey) { + if (ctx->started) { + mdb_cursor_close(ctx->crsr); + mdb_dbi_close(ctx->env, ctx->dbi); + mdb_txn_abort(ctx->tx); + } + + r = mdb_txn_begin(ctx->env, NULL, MDB_RDONLY, &ctx->tx); + if (r) { + return ERR_DB_FAIL; + } + r = mdb_dbi_open(ctx->tx, NULL, 0, &ctx->dbi); + if (r) { + return ERR_DB_FAIL; + } + + r = mdb_cursor_open(ctx->tx, ctx->dbi, &ctx->crsr); + if (r) { + return ERR_DB_FAIL; + } + ctx->current_key = pfx; + + start[0] = (char)pfx; + ctx->k.mv_size = 1; + if (!ctx->browsing) { + if (*key != 0) { + memcpy(start+1, *key, *key_len); + ctx->k.mv_size += *key_len; + } + } + ctx->k.mv_data = start; + } + + if (!ctx->browsing) { + r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_SET_RANGE); + ctx->browsing = 1; + } else { + r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_NEXT_NODUP); + } + if (r) { + return ERR_DB_FAIL; + } + start[0] = (char)*((char*)ctx->k.mv_data); + if (start[0] != ctx->current_key) { + db_reset(ctx); + return ERR_DB_NOMATCH; + } + + *key = (char*)ctx->k.mv_data; + *key_len = ctx->k.mv_size; + *value = (char*)ctx->v.mv_data; + *value_len = ctx->v.mv_size; + + return ERR_OK; + +} + + +void db_reset(struct db_ctx *ctx) { + memset(ctx, 0, sizeof(struct db_ctx)); +} diff --git a/src/db.h b/src/db.h @@ -0,0 +1,50 @@ +#ifndef _DB_H +#define _DB_H + +#include <lmdb.h> + +#ifndef DB_KEY_SIZE_LIMIT +#define DB_KEY_SIZE_LIMIT 64 +#endif + +#ifndef DB_VALUE_SIZE_LIMIT +#define DB_VALUE_SIZE_LIMIT 1048576 +#endif + +/** + * \brief Key prefixes used for database storage. + * + */ +enum DbKey { + /// Noop value, used for default value of a DbKey variable + DbNoKey = 0x00, + /// A credit item record + DbKeyCreditItem = 0x01, + /// A reverse lookup record; resolves the content hash to the content entry in the database. + DbKeyReverse = 0xff, +}; + +/** + * + * \brief Interface to persistent storage of data items used in application. + * + */ +struct db_ctx { + char *connstr; + MDB_env *env; + MDB_dbi dbi; + MDB_txn *tx; + MDB_cursor *crsr; + MDB_val k; + MDB_val v; + enum DbKey current_key; + int started; + int browsing; +}; + +int db_connect(struct db_ctx *ctx, char *conn); +int db_put(struct db_ctx *ctx, enum DbKey pfx, char *data, size_t data_len); +int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, char **value, size_t *value_len); +void db_reset(struct db_ctx *ctx); + +#endif // _DB_H diff --git a/src/debug.c b/src/debug.c @@ -0,0 +1,20 @@ +#include <gtk/gtk.h> + +#include "debug.h" + + +void debug_log(enum debugLevel level, const char *s) { + int loglevel; + + switch(level) { + case DEBUG_CRITICAL: + loglevel = G_LOG_LEVEL_ERROR; + break; + case DEBUG_ERROR: + loglevel = G_LOG_LEVEL_ERROR; + break; + default: + loglevel = G_LOG_LEVEL_DEBUG; + } + g_log(G_LOG_DOMAIN, loglevel, s); +} diff --git a/src/debug.h b/src/debug.h @@ -0,0 +1,39 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +/** + * \brief Debug levels for simple log output. + */ +enum debugLevel { + /// Critical error, should terminate application. + DEBUG_CRITICAL, + /// Error, anomalous condition that should not occur. + DEBUG_ERROR, + /// Warning, condition that may contribute to affecting the state of the program adversely. + DEBUG_WARNING, + /// Info, events that an end-user may be interested in during normal operation. + DEBUG_INFO, + /// Debug, events that a developer may be intereted in during normal operation. + DEBUG_DEBUG, + /// Trace, mostly temporary loglines used for debugging concrete issues during development. + DEBUG_TRACE, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * \brief Implementer logs constant string according to given log level. + * + * \param level Debug level + * \param s String to log + */ +void debug_log(enum debugLevel level, const char *s); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/digest.c b/src/digest.c @@ -0,0 +1,27 @@ +#include <gcrypt.h> + +#include "err.h" +#include "digest.h" + + +int calculate_digest(const char *in, size_t in_len, char *out) { + gcry_error_t e; + gcry_md_hd_t h; + unsigned char *v; + static unsigned int digest_len; + + if (digest_len == 0) { + digest_len = gcry_md_get_algo_dlen(GCRY_MD_SHA256); + } + + e = gcry_md_open(&h, GCRY_MD_SHA256, GCRY_MD_FLAG_SECURE); + if (e) { + return ERR_DIGESTFAIL; + } + + gcry_md_write(h, in, in_len); + v = gcry_md_read(h, 0); + memcpy(out, v, digest_len); + gcry_md_close(h); + return ERR_OK; +} diff --git a/src/digest.h b/src/digest.h @@ -0,0 +1,23 @@ +#ifndef _KEE_DIGEST_H +#define _KEE_DIGEST_H + +#ifdef __cplusplus +extern "C" { +#endif + /** + * + * \brief Calculate SHA256 digest over the given input data. + * + * \param in Input data. + * \param in_len Length of input data. + * \param out Contains digest data on success. Must have at least 32 bytes available. + * \return 0 on success, any other value indicates failure. + * + */ + int calculate_digest(const char *in, size_t in_len, char *out); + +#ifdef __cplusplus +} +#endif + +#endif // _KEE_DIGEST_H diff --git a/src/endian.c b/src/endian.c @@ -0,0 +1,44 @@ +#include "endian.h" + +int is_le() { + unsigned short s = 42; + return *((unsigned char*)&s) == 42; +} + +// convert unsigned integer to little-endian representation +int to_endian(char direction, int l, void *n) { + union le un; + + if (l == 1 || is_le() == direction) { + return 0; + } + switch(l) { + case sizeof(long long): + un.ll = (long long*)n; + break; + case sizeof(int): + un.i = (int*)n; + break; + case sizeof(short): + un.s = (short*)n; + break; + default: + return 1; + } + flip_endian(l, &un); + + return 0; +} + +void flip_endian(int l, union le *n) { + int i; + char t; + unsigned char *ne; + + ne = (n->c)+(l-1); + for (i = 0; i < l/2; i++) { + t = *(n->c+i); + *((n->c)+i) = *(ne-i); + *(ne-i) = t; + } +} diff --git a/src/endian.h b/src/endian.h @@ -0,0 +1,45 @@ +#ifndef LASH_ENDIAN_H_ +#define LASH_ENDIAN_H_ + +/** + * + * \brief Encapsulats all suppoerted integer lengths for endian conversion. + * + */ +union le { + short *s; + int *i; + long long *ll; + unsigned char *c; +}; + +//int le(int l, void *n); +/* + * Return true (1) if system is little-endian. + */ +int is_le(); +/** + * Convert to specified endian order. + * + * The integer data in \c n is changed in-place. + * + * If \c direction is same as system endianness, or \c l==1, no action is taken. + * + * \param direction 0 for big-endian, 1 for little-endian. + * \param l Length of integer \c n in bytes. + * \param n Integer data. + * \return 1 if \c l is invalid byte size, 0 (success) otherwise. + * + */ +int to_endian(char direction, int l, void *n); +/** + * Change endian order of given number. + * + * The integer data in \c n is changed in-place. + * + * \param l Length of integer \c in bytes. + * \param n Integer data. + */ +void flip_endian(int l, union le *n); + +#endif // LASH_ENDIAN_H_ diff --git a/src/err.h b/src/err.h @@ -0,0 +1,19 @@ +/** + * + * Error codes within context of the kee application and backend. + * + */ +enum keeError { + ERR_OK, + ERR_FAIL, + ERR_SPACE, + ERR_NOCRYPTO, + ERR_NOKEY, + ERR_KEYFAIL, + ERR_DIGESTFAIL, + ERR_DB_FAIL, + ERR_DB_NOMATCH, + ERR_DB_INVALID, + ERR_QR_MISSING, + ERR_QR_INVALID, +}; diff --git a/src/export.c b/src/export.c @@ -0,0 +1,114 @@ +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <varint.h> + +#include "export.h" +#include "common.h" +#include "err.h" + + +int export_init(struct kee_export *ex, size_t count) { + size_t unit; + + ex->count = count; + + // 10 is max varint + unit = sizeof(ex->lenlens) + sizeof(ex->data) + sizeof(ex->datalens) + 10; + ex->lens = malloc(unit * count); + if (ex->lens == NULL) { + return 1; + } + unit += sizeof(ex->lens) * count; + ex->lenlens = (size_t*)ex->lens + unit; + unit += sizeof(ex->lenlens) * count; + ex->data = (char**)ex->lens + unit; + unit += sizeof(ex->data) * count; + ex->datalens = (size_t*)ex->lens + unit; + return ERR_OK; +} + +int export_add_item(struct kee_export *ex, char *in, size_t in_len) { + int r; + char b[10]; + + if (in_len == 0) { + return ERR_FAIL; + } + + r = 0; + r += varint_write_u(b, in_len); + memcpy(ex->lens+(ex->count*10), b, 10); + *(ex->lenlens + sizeof(ex->lenlens) * ex->count) = (size_t)r; + *(ex->datalens + sizeof(ex->datalens) * ex->count) = in_len; + *(ex->data + sizeof(ex->data) * ex->count) = in; + + ex->size += r; + ex->count++; + return r; + +} + +int export_write(struct kee_export *ex, char *out, size_t out_len) { + char *p; + int i; + size_t l; + size_t c; + + if (out_len < ex->size) { + return -1; + } + + p = out; + c = 0; + for (i = 0; i < ex->count; i++) { + l = ex->lenlens[i]; + memcpy(p, ex->lens+(10*i), l); + p += l; + c += l; + l = ex->datalens[i]; + memcpy(p, ex->data[i], l); + p += l; + c += l; + } + return c; +} + + +void export_free(struct kee_export *ex) { + free(ex->lens); +} + +// data not copied, must be in scope for duration +int import_init(struct kee_import *im, const char *in, size_t in_len) { + im->data = (char*)in; + im->size = in_len; + im->crsr = 0; + im->eof = 0; + return ERR_OK; +} + +kee_boolean done(struct kee_import *im) { + return im->eof; +} + +int import_read(struct kee_import *im, char *out, size_t out_len) { + int r; + uint64_t l; + + if (im->size - im->crsr <= 0) { + im->eof = 1; + return 0; + } + + r = varint_read_u(im->data + im->crsr, out_len, &l); + im->crsr += r; + + memcpy(out, im->data + im->crsr, (size_t)l); + im->crsr += l; + + return l; +} + +void import_free(struct kee_import *im) { +} diff --git a/src/export.h b/src/export.h @@ -0,0 +1,36 @@ +#ifndef _EXPORT_H +#define _EXPORT_H + +#include <stddef.h> +#include <stdint.h> + +#include "common.h" + +struct kee_export { + size_t cap; + int count; + size_t size; + char *lens; + size_t *lenlens; + char **data; + size_t *datalens; +}; + +struct kee_import { + char *data; + size_t size; + unsigned int crsr; + kee_boolean eof; +}; + +int export_init(struct kee_export *ex, size_t count); +void export_free(struct kee_export *ex); +int export_add_item(struct kee_export *ex, char *in, size_t in_len); +int export_write(struct kee_export *ex, char *out, size_t out_len); + +int import_init(struct kee_import *im, const char *in, size_t in_len); +void import_free(struct kee_import *im); +int import_read(struct kee_import *im, char *out, size_t out_len); +kee_boolean done(struct kee_import *im); + +#endif // _EXPORT_H diff --git a/src/gpg.h b/src/gpg.h @@ -0,0 +1,117 @@ +#ifndef _KEE_GPG_H +#define _KEE_GPG_H +#include <string> +#include <stddef.h> + +#define GPG_MIN_VERSION "1.10.2" +#define CHACHA20_KEY_LENGTH_BYTES 32 +#define CHACHA20_NONCE_LENGTH_BYTES 12 + +#ifndef ENCRYPT_BLOCKSIZE +#define ENCRYPT_BLOCKSIZE 4096 +#endif + +/** + * + * \brief Encrypt the given string data with the provided encryption key and nonce. + * + * \sa encryptb + * + */ +int encrypt(char *ciphertext, size_t ciphertext_len, std::string indata, const char *key, const char *nonce); + +/** + * + * Encrypt the given binary data with the provided encryption key and nonce, using the CHACHA20-POLY1305 algorithm. + * + * If successful, \c ciphertext and \c ciphertext_len hold the encrypted data and its length respectively. + * + * \param ciphertext holds resulting ciphertext + * \param ciphertext_len holds the length of the resulting ciphertext + * \param indata input data to encrypt + * \param indata_len length of input data to encrypt + * \param key 256-bit key to use for encryption + * \param nonce 256-bit nonce to use as salt for encryption + * \return 0 on successful encryption. + * + */ +int encryptb (char *ciphertext, size_t ciphertext_len, const char *indata, size_t indata_len, const char *key, const char *nonce); + +/** + * + * Decrypt the given string ciphertext with the provided encryption key and nonce. + * + * \sa decryptb + * + */ +int decrypt(std::string *outdata, const char *ciphertext, size_t ciphertext_len, const char *key, const char *nonce); + +/** + * + * Decrypt the given binary ciphertext with the provided encryption key and nonce, using the CHACHA20-POLY1305 algorithm. + * + * If successful, the decrypted (plaintext) data will be stored in \c outdata. + * + * \param outdata holdes the resulting plaintext + * \param ciphertext encrypted data to decrypt + * \param ciphertext_len length of encrypted data + * \param key 256-bit key to use for decryption + * \param nonce 256-bit nonce to use as salt for decryption + * \return 0 on successful decryption. + * + */ +int decryptb(char *outdata, const char *ciphertext, size_t ciphertext_len, const char *key, const char *nonce); + +/** + * + * Calculate number of bytes a given byte count when rounded up to the given block size. + * + * For example, if \c blocksize is \c 5 and \c insize is \c 11, then \c 15 will be returned. + * + * \param insize size of data + * \param blocksize block size to round up to + * + */ +size_t get_padsize(size_t insize, size_t blocksize); + +/** + * + * \brief Interface to the encrypted key storage for both identity public key and the key used for encryption of the identity public key. + * + */ +class GpgStore { + + public: + /// Sets correct context values for underlying \c gcrypt operations. + GpgStore(); + /** + * + * Attempts to decrypt the identity public key with the given passphrase. + * + * If no public key exists, one will be created and encrypted using the passphrase. + * + * \param p path to key store + * \param passphrase passphrase for public key encryption + * \return 0 if successful, any other value indicates an error + * + */ + int check(std::string p, std::string passphrase); + /** + * + * Returns the fingerprint of the identity public key. + * + * \return 160-bit fingerprint value + */ + char *get_fingerprint(); + + private: + /// calculates sha256 digest for the given string value, using secure memory + int digest(char *out, std::string in); + //const char *m_version; + //char *m_seckey; + /// cached fingerprint value, in string format with zero terminator + char m_fingerprint[41]; + /// cached digest length of sha256 + unsigned int m_passphrase_digest_len; +}; +#endif diff --git a/src/hex.c b/src/hex.c @@ -0,0 +1,21 @@ +#include <stddef.h> +#include <stdio.h> + +#include "hex.h" + + +int bin_to_hex(const unsigned char *data, size_t l, unsigned char *zHex, size_t *z) { + unsigned int i; + + if (*z < (l*2)+1) { + return 1; + } + + for (i = 0; i < l; i++) { + sprintf((char*)zHex+(i*2), "%02x", *(data+i)); + } + *z = (i*2); + *(zHex+(*z)) = 0x0; + *z = *z + 1; + return 0; +} diff --git a/src/hex.h b/src/hex.h @@ -0,0 +1,22 @@ +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * \brief Convert data to hex string + * + * \param data Input data. + * \param l Input data length. + * \param zHex If successful, contains hex string output. + * \param z Pointer to available length of \c zHex. Will contain the string length after succesful conversion. + * \todo \c z output is superfluous as zHex will (should) be zero-terminated. + * \return 1 if Output buffer is insufficient to write the result string, otherwise 0 (success). + */ +int bin_to_hex(const unsigned char *data, size_t l, unsigned char *zHex, size_t *z); + +#ifdef __cplusplus +} +#endif diff --git a/src/item.h b/src/item.h @@ -0,0 +1,6 @@ +#ifndef _KEE_ITEM_H +#define _KEE_ITEM_H + +useritem_preview(void 'item); + +#endif // _KEE_ITEM_H diff --git a/src/kee.gresource.xml b/src/kee.gresource.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<gresources> + <gresource prefix="/org/defalsify/Kee"> + <file preprocess="xml-stripblanks">main.ui</file> + </gresource> +</gresources> diff --git a/src/main.c b/src/main.c @@ -0,0 +1,80 @@ +#include <string.h> +#include <gtk/gtk.h> +#include "ui.h" +#include "db.h" + + +static void act_scan(GSimpleAction *act, GVariant *param, GApplication *app) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "scan clicked"); +} + +static void act_quit(GSimpleAction *act, GVariant *param, GApplication *app) { + g_application_quit(app); +} + +static void startup(GtkApplication *app, gpointer user_data) { + GMenu *menu_bar; + GMenu *menu; + GMenuItem *menu_item; + GMenuItem *menu_item_menu; + GSimpleAction *act; + + menu_bar = g_menu_new(); + menu_item_menu = g_menu_item_new("Menu", NULL); + menu = g_menu_new(); + menu_item = g_menu_item_new("Scan", "app.scan"); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + + menu_item = g_menu_item_new("Quit", "app.quit"); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + + act = g_simple_action_new("quit", NULL); + g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(act)); + g_signal_connect(act, "activate", G_CALLBACK(act_quit), app); + + act = g_simple_action_new("scan", NULL); + g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(act)); + g_signal_connect(act, "activate", G_CALLBACK(act_scan), app); + + g_menu_item_set_submenu(menu_item_menu, G_MENU_MODEL(menu)); + g_object_unref(menu); + + g_menu_append_item(menu_bar, menu_item_menu); + g_object_unref(menu_item_menu); + + gtk_application_set_menubar(GTK_APPLICATION(app), G_MENU_MODEL(menu_bar)); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "set up menus"); +} + +static void activate(GtkApplication *app, gpointer user_data) { + struct ui_container *ui; + + ui = (struct ui_container*)user_data; + ui_build(app, ui); +} + +static void deactivate(GtkApplication *app, gpointer user_data) { + struct ui_container *ui; + + ui = (struct ui_container*)user_data; + ui_free(ui); +} + +int main(int argc, char **argv) { + struct ui_container ui; + int r; + GtkApplication *app; + struct db_ctx db; + + db_connect(&db, "./testdata_mdb"); + + app = gtk_application_new ("no.holbrook.example.Buidler", G_APPLICATION_DEFAULT_FLAGS); + g_signal_connect (app, "startup", G_CALLBACK (startup), &ui); + g_signal_connect (app, "activate", G_CALLBACK (activate), &ui); + g_signal_connect (app, "shutdown", G_CALLBACK (deactivate), &ui); + r = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref(app); + return r; +} diff --git a/src/resources.c b/src/resources.c @@ -0,0 +1,324 @@ +#include <gio/gio.h> + +#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6)) +# define SECTION __attribute__ ((section (".gresource.kee"), aligned (8))) +#else +# define SECTION +#endif + +static const SECTION union { const guint8 data[1881]; const double alignment; void * const ptr;} kee_resource_data = { + "\107\126\141\162\151\141\156\164\000\000\000\000\000\000\000\000" + "\030\000\000\000\254\000\000\000\000\000\000\050\005\000\000\000" + "\000\000\000\000\003\000\000\000\004\000\000\000\004\000\000\000" + "\005\000\000\000\113\120\220\013\002\000\000\000\254\000\000\000" + "\004\000\114\000\260\000\000\000\264\000\000\000\161\002\354\244" + "\000\000\000\000\264\000\000\000\012\000\114\000\300\000\000\000" + "\304\000\000\000\324\265\002\000\377\377\377\377\304\000\000\000" + "\001\000\114\000\310\000\000\000\314\000\000\000\206\165\217\153" + "\004\000\000\000\314\000\000\000\007\000\166\000\330\000\000\000" + "\117\007\000\000\065\256\104\147\001\000\000\000\117\007\000\000" + "\004\000\114\000\124\007\000\000\130\007\000\000\157\162\147\057" + "\001\000\000\000\144\145\146\141\154\163\151\146\171\057\000\000" + "\004\000\000\000\057\000\000\000\000\000\000\000\155\141\151\156" + "\056\165\151\000\000\000\000\000\147\006\000\000\000\000\000\000" + "\074\077\170\155\154\040\166\145\162\163\151\157\156\075\042\061" + "\056\060\042\040\145\156\143\157\144\151\156\147\075\042\125\124" + "\106\055\070\042\077\076\012\074\151\156\164\145\162\146\141\143" + "\145\076\074\162\145\161\165\151\162\145\163\040\154\151\142\075" + "\042\147\164\153\042\040\166\145\162\163\151\157\156\075\042\064" + "\056\060\042\057\076\074\157\142\152\145\143\164\040\143\154\141" + "\163\163\075\042\107\164\153\127\151\156\144\157\167\042\040\151" + "\144\075\042\153\145\145\143\150\157\157\163\145\042\076\074\160" + "\162\157\160\145\162\164\171\040\156\141\155\145\075\042\143\150" + "\151\154\144\042\076\074\157\142\152\145\143\164\040\143\154\141" + "\163\163\075\042\107\164\153\114\151\163\164\102\157\170\042\057" + "\076\074\057\160\162\157\160\145\162\164\171\076\074\057\157\142" + "\152\145\143\164\076\074\157\142\152\145\143\164\040\143\154\141" + "\163\163\075\042\107\164\153\127\151\156\144\157\167\042\040\151" + "\144\075\042\153\145\145\163\143\141\156\042\076\074\160\162\157" + "\160\145\162\164\171\040\156\141\155\145\075\042\143\150\151\154" + "\144\042\076\074\157\142\152\145\143\164\040\143\154\141\163\163" + "\075\042\107\164\153\102\157\170\042\076\074\160\162\157\160\145" + "\162\164\171\040\156\141\155\145\075\042\157\162\151\145\156\164" + "\141\164\151\157\156\042\076\166\145\162\164\151\143\141\154\074" + "\057\160\162\157\160\145\162\164\171\076\074\143\150\151\154\144" + "\076\074\157\142\152\145\143\164\040\143\154\141\163\163\075\042" + "\107\164\153\111\155\141\147\145\042\076\074\160\162\157\160\145" + "\162\164\171\040\156\141\155\145\075\042\151\143\157\156\055\156" + "\141\155\145\042\076\147\164\153\055\155\151\163\163\151\156\147" + "\055\151\155\141\147\145\074\057\160\162\157\160\145\162\164\171" + "\076\074\160\162\157\160\145\162\164\171\040\156\141\155\145\075" + "\042\165\163\145\055\146\141\154\154\142\141\143\153\042\076\061" + "\074\057\160\162\157\160\145\162\164\171\076\074\057\157\142\152" + "\145\143\164\076\074\057\143\150\151\154\144\076\074\143\150\151" + "\154\144\076\074\160\154\141\143\145\150\157\154\144\145\162\057" + "\076\074\057\143\150\151\154\144\076\074\143\150\151\154\144\076" + "\074\160\154\141\143\145\150\157\154\144\145\162\057\076\074\057" + "\143\150\151\154\144\076\074\057\157\142\152\145\143\164\076\074" + "\057\160\162\157\160\145\162\164\171\076\074\057\157\142\152\145" + "\143\164\076\074\157\142\152\145\143\164\040\143\154\141\163\163" + "\075\042\107\164\153\127\151\156\144\157\167\042\040\151\144\075" + "\042\153\145\145\165\156\154\157\143\153\042\076\074\160\162\157" + "\160\145\162\164\171\040\156\141\155\145\075\042\143\150\151\154" + "\144\042\076\074\157\142\152\145\143\164\040\143\154\141\163\163" + "\075\042\107\164\153\102\157\170\042\040\151\144\075\042\153\145" + "\145\165\156\154\157\143\153\142\157\170\042\076\074\160\162\157" + "\160\145\162\164\171\040\156\141\155\145\075\042\157\162\151\145" + "\156\164\141\164\151\157\156\042\076\166\145\162\164\151\143\141" + "\154\074\057\160\162\157\160\145\162\164\171\076\074\143\150\151" + "\154\144\076\074\157\142\152\145\143\164\040\143\154\141\163\163" + "\075\042\107\164\153\105\156\164\162\171\042\040\151\144\075\042" + "\153\145\145\165\156\154\157\143\153\160\141\163\163\160\150\162" + "\141\163\145\042\076\074\160\162\157\160\145\162\164\171\040\156" + "\141\155\145\075\042\146\157\143\165\163\141\142\154\145\042\076" + "\061\074\057\160\162\157\160\145\162\164\171\076\074\057\157\142" + "\152\145\143\164\076\074\057\143\150\151\154\144\076\074\143\150" + "\151\154\144\076\074\157\142\152\145\143\164\040\143\154\141\163" + "\163\075\042\107\164\153\102\165\164\164\157\156\042\040\151\144" + "\075\042\153\145\145\165\156\154\157\143\153\163\165\142\155\151" + "\164\042\076\074\160\162\157\160\145\162\164\171\040\156\141\155" + "\145\075\042\154\141\142\145\154\042\040\164\162\141\156\163\154" + "\141\164\141\142\154\145\075\042\061\042\076\142\165\164\164\157" + "\156\074\057\160\162\157\160\145\162\164\171\076\074\160\162\157" + "\160\145\162\164\171\040\156\141\155\145\075\042\146\157\143\165" + "\163\141\142\154\145\042\076\061\074\057\160\162\157\160\145\162" + "\164\171\076\074\160\162\157\160\145\162\164\171\040\156\141\155" + "\145\075\042\162\145\143\145\151\166\145\163\055\144\145\146\141" + "\165\154\164\042\076\061\074\057\160\162\157\160\145\162\164\171" + "\076\074\057\157\142\152\145\143\164\076\074\057\143\150\151\154" + "\144\076\074\143\150\151\154\144\076\074\160\154\141\143\145\150" + "\157\154\144\145\162\057\076\074\057\143\150\151\154\144\076\074" + "\057\157\142\152\145\143\164\076\074\057\160\162\157\160\145\162" + "\164\171\076\074\057\157\142\152\145\143\164\076\074\157\142\152" + "\145\143\164\040\143\154\141\163\163\075\042\107\164\153\127\151" + "\156\144\157\167\042\040\151\144\075\042\153\145\145\166\151\145" + "\167\042\076\074\160\162\157\160\145\162\164\171\040\156\141\155" + "\145\075\042\143\150\151\154\144\042\076\074\157\142\152\145\143" + "\164\040\143\154\141\163\163\075\042\107\164\153\107\162\151\144" + "\042\040\151\144\075\042\166\151\145\167\042\076\074\143\150\151" + "\154\144\076\074\157\142\152\145\143\164\040\143\154\141\163\163" + "\075\042\107\164\153\111\155\141\147\145\042\076\074\160\162\157" + "\160\145\162\164\171\040\156\141\155\145\075\042\165\163\145\055" + "\146\141\154\154\142\141\143\153\042\076\061\074\057\160\162\157" + "\160\145\162\164\171\076\074\154\141\171\157\165\164\076\074\160" + "\162\157\160\145\162\164\171\040\156\141\155\145\075\042\143\157" + "\154\165\155\156\042\076\060\074\057\160\162\157\160\145\162\164" + "\171\076\074\160\162\157\160\145\162\164\171\040\156\141\155\145" + "\075\042\162\157\167\042\076\060\074\057\160\162\157\160\145\162" + "\164\171\076\074\160\162\157\160\145\162\164\171\040\156\141\155" + "\145\075\042\143\157\154\165\155\156\055\163\160\141\156\042\076" + "\062\074\057\160\162\157\160\145\162\164\171\076\074\160\162\157" + "\160\145\162\164\171\040\156\141\155\145\075\042\162\157\167\055" + "\163\160\141\156\042\076\062\074\057\160\162\157\160\145\162\164" + "\171\076\074\057\154\141\171\157\165\164\076\074\057\157\142\152" + "\145\143\164\076\074\057\143\150\151\154\144\076\074\143\150\151" + "\154\144\076\074\160\154\141\143\145\150\157\154\144\145\162\057" + "\076\074\057\143\150\151\154\144\076\074\143\150\151\154\144\076" + "\074\160\154\141\143\145\150\157\154\144\145\162\057\076\074\057" + "\143\150\151\154\144\076\074\143\150\151\154\144\076\074\160\154" + "\141\143\145\150\157\154\144\145\162\057\076\074\057\143\150\151" + "\154\144\076\074\143\150\151\154\144\076\074\160\154\141\143\145" + "\150\157\154\144\145\162\057\076\074\057\143\150\151\154\144\076" + "\074\143\150\151\154\144\076\074\160\154\141\143\145\150\157\154" + "\144\145\162\057\076\074\057\143\150\151\154\144\076\074\057\157" + "\142\152\145\143\164\076\074\057\160\162\157\160\145\162\164\171" + "\076\074\057\157\142\152\145\143\164\076\074\057\151\156\164\145" + "\162\146\141\143\145\076\012\000\000\050\165\165\141\171\051\113" + "\145\145\057\000\003\000\000\000" }; + +static GStaticResource static_resource = { kee_resource_data.data, sizeof (kee_resource_data.data) - 1 /* nul terminator */, NULL, NULL, NULL }; + +G_MODULE_EXPORT +GResource *kee_get_resource (void); +GResource *kee_get_resource (void) +{ + return g_static_resource_get_resource (&static_resource); +} +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __G_CONSTRUCTOR_H__ +#define __G_CONSTRUCTOR_H__ + +/* + If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and + destructors, in a usable way, including e.g. on library unload. If not you're on + your own. + + Some compilers need #pragma to handle this, which does not work with macros, + so the way you need to use this is (for constructors): + + #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA + #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor) + #endif + G_DEFINE_CONSTRUCTOR(my_constructor) + static void my_constructor(void) { + ... + } + +*/ + +#ifndef __GTK_DOC_IGNORE__ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); +#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); + +#elif defined (_MSC_VER) && (_MSC_VER >= 1500) +/* Visual studio 2008 and later has _Pragma */ + +/* + * Only try to include gslist.h if not already included via glib.h, + * so that items using gconstructor.h outside of GLib (such as + * GResources) continue to build properly. + */ +#ifndef __G_LIB_H__ +#include "gslist.h" +#endif + +#include <stdlib.h> + +#define G_HAS_CONSTRUCTORS 1 + +/* We do some weird things to avoid the constructors being optimized + * away on VS2015 if WholeProgramOptimization is enabled. First we + * make a reference to the array from the wrapper to make sure its + * references. Then we use a pragma to make sure the wrapper function + * symbol is always included at the link stage. Also, the symbols + * need to be extern (but not dllexport), even though they are not + * really used from another object file. + */ + +/* We need to account for differences between the mangling of symbols + * for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed + * with an underscore but symbols on x64/ARM/ARM64 are not. + */ +#ifdef _M_IX86 +#define G_MSVC_SYMBOL_PREFIX "_" +#else +#define G_MSVC_SYMBOL_PREFIX "" +#endif + +#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX) +#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX) + +#define G_MSVC_CTOR(_func,_sym_prefix) \ + static void _func(void); \ + extern int (* _array ## _func)(void); \ + int _func ## _wrapper(void); \ + int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \ + __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper; + +#define G_MSVC_DTOR(_func,_sym_prefix) \ + static void _func(void); \ + extern int (* _array ## _func)(void); \ + int _func ## _constructor(void); \ + int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \ + __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined (_MSC_VER) + +#define G_HAS_CONSTRUCTORS 1 + +/* Pre Visual studio 2008 must use #pragma section */ +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _wrapper(void) { _func(); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper; + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _constructor(void) { atexit (_func); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined(__SUNPRO_C) + +/* This is not tested, but i believe it should work, based on: + * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c + */ + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + init(_func) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + fini(_func) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); + +#else + +/* constructors not supported for this compiler */ + +#endif + +#endif /* __GTK_DOC_IGNORE__ */ +#endif /* __G_CONSTRUCTOR_H__ */ + +#ifdef G_HAS_CONSTRUCTORS + +#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA +#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(keeresource_constructor) +#endif +G_DEFINE_CONSTRUCTOR(keeresource_constructor) +#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA +#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(keeresource_destructor) +#endif +G_DEFINE_DESTRUCTOR(keeresource_destructor) + +#else +#warning "Constructor not supported on this compiler, linking in resources will not work" +#endif + +static void keeresource_constructor (void) +{ + g_static_resource_init (&static_resource); +} + +static void keeresource_destructor (void) +{ + g_static_resource_fini (&static_resource); +} diff --git a/src/rpc.h b/src/rpc.h @@ -0,0 +1,19 @@ +#ifndef _KEE_RPC_H +#define _KEE_RPC_H + +#include <gtk/gtk.h> + +#ifndef RPC_BUFFER_SIZE +#define RPC_BUFFER_SIZE 4096 +#endif + +#ifndef RPC_COMMAND_SIZE +#define RPC_COMMAND_SIZE 1048576 +#endif + + +struct kee_rpc { + GApplication *gap; +} + +#endif // _KEE_RPC_H diff --git a/src/settings.c b/src/settings.c @@ -0,0 +1,67 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <basedir.h> +#include <sys/stat.h> + +#include "err.h" +#include "debug.h" +#include "settings.h" + + +/** + * \todo make xdg optional + */ +int settings_new_from_xdg(struct kee_settings *z) { + return ERR_OK; +} + +int settings_init(struct kee_settings *z) { + int r; + char s[1024]; + + r = mkdir((char*)z->data, S_IRUSR | S_IWUSR); + if (r) { + if (errno != EEXIST) { + debug_log(DEBUG_ERROR, strerror(errno)); + return r; + } + } + sprintf(s, "datadir: %s\nrundir: %s", z->data, z->run); + debug_log(DEBUG_DEBUG, s); + + return ERR_OK; +} + +unsigned char *settings_get(struct kee_settings *z, enum SettingsType typ) { + switch(typ) { + case SETTINGS_DATA: + return z->data; + break; + case SETTINGS_RUN: + return z->run; + break; + case SETTINGS_LOCKTIME: + return z->locktime; + break; + default: + return (unsigned char*)""; + } +} + +int settings_set(struct kee_settings *z, enum SettingsType typ, unsigned char* v) { + switch(typ) { + case SETTINGS_DATA: + z->data = v; + break; + case SETTINGS_RUN: + z->run = v; + break; + case SETTINGS_LOCKTIME: + z->locktime = v; + break; + default: + return ERR_FAIL; + } + return ERR_OK; +} diff --git a/src/settings.h b/src/settings.h @@ -0,0 +1,54 @@ +#ifndef _KEE_SETTINGS +#define _KEE_SETTINGS + + +/** + * \brief Encapsulates settings for application. + * + */ +struct kee_settings { + unsigned char *data; + unsigned char *run; + unsigned char *locktime; +}; + +/** + * + * \brief Numeric code used to set and get specific settings items. + */ +enum SettingsType { + /// Data directory + SETTINGS_DATA = 0x01, + /// Runtime directory + SETTINGS_RUN = 0x02, + /// Milliseconds a key will stay unlocked since last application use. + SETTINGS_LOCKTIME = 0x10, +}; + +/** + * \param Prepare system for reading and/or writing settings. + * + * Create necessary directories. + * + * \return ERR_OK if successful, or \c errno of any error the has occurred. + */ +int settings_init(struct kee_settings *z); + +/** + * \brief Retrieve a single settings entry. + * + * \param typ Settings entry type to retrieve. + * \return Result string value. An empty string is returned if no value is set. + */ +unsigned char *settings_get(struct kee_settings *z, enum SettingsType typ); + +/** + * \brief Set a single settings entry. + * + * \param typ Settings entry type to set. + * \param v Value to set for entry. + * \return ERR_OK if successful, ERR_FAIL if invalid settings type. + */ +int settings_set(struct kee_settings *z, enum SettingsType typ, unsigned char* v); + +#endif // KEE_SETTINGS diff --git a/src/term_debug.c b/src/term_debug.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "debug.h" + + +void debugLog(enum debugLevel level, const char *s) { + fprintf(stderr, "%d: %s\n", level, s); +} diff --git a/src/transport.c b/src/transport.c @@ -0,0 +1,143 @@ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <b64/cencode.h> +#include <b64/cdecode.h> +#include <zlib.h> +#include "transport.h" +#include "err.h" + + +static int pack_compress(char *in, size_t in_len, char *out, size_t *out_len) { + int r; + z_stream z; + + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + z.data_type = Z_TEXT; + z.next_in = (unsigned char*)in; + z.avail_in = in_len; + z.next_out = (unsigned char*)out; + z.avail_out = *out_len; + + r = deflateInit(&z, Z_BEST_COMPRESSION); + if (r != Z_OK) { + return 1; + } + r = deflate(&z, Z_FINISH); + if (r != Z_STREAM_END) { + return 2; + } + *out_len = z.total_out; + + return 0; +} + +static int pack_encode(char *in, size_t in_len, char *out, size_t *out_len) { + char *p; + int r; + base64_encodestate bst; + + base64_init_encodestate(&bst); + + p = out; + r = base64_encode_block(in, in_len, p, &bst); + if (r == 0) { + return 1; + } + *out_len = r; + p += r; + r = base64_encode_blockend(p, &bst); + if (r == 0) { + return 1; + } + *out_len += r; + + return 0; + +} + +static int unpack_decompress(char *in, size_t in_len, char *out, size_t *out_len) { + int r; + z_stream z; + + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + z.data_type = Z_TEXT; + z.next_in = (unsigned char*)in; + z.avail_in = in_len; + z.next_out = (unsigned char*)out; + z.avail_out = *out_len; + + r = inflateInit(&z); + if (r != Z_OK) { + return 1; + } + r = inflate(&z, Z_FINISH); + if (r != Z_STREAM_END) { + return 2; + } + *out_len = z.total_out; + + return 0; +} + +static int unpack_decode(char *in, size_t in_len, char *out, size_t *out_len) { + int r; + base64_decodestate dst; + + base64_init_decodestate(&dst); + r = base64_decode_block(in, in_len, out, &dst); + if (r == 0) { + return 1; + } + *out_len = r; + + return 0; +} + +int pack(char *in, size_t in_len, char *out, size_t *out_len) { + int r; + char *buf; + + buf = malloc(*out_len); + + r = pack_compress(in, in_len, buf, out_len); + if (r) { + free(buf); + return ERR_FAIL; + } + + r = pack_encode(buf, *out_len, out, out_len); + if (r) { + free(buf); + return ERR_FAIL; + } + + free(buf); + return ERR_OK; +} + +int unpack(char *in, size_t in_len, char *out, size_t *out_len) { + int r; + char *buf; + + buf = malloc(*out_len); + + r = unpack_decode(in, in_len, buf, out_len); + if (r) { + free(buf); + return ERR_FAIL; + } + + r = unpack_decompress(buf, *out_len, out, out_len); + if (r) { + free(buf); + return ERR_FAIL; + } + + free(buf); + return ERR_OK; +} diff --git a/src/transport.h b/src/transport.h @@ -0,0 +1,40 @@ +#ifndef _TRANSPORT_H +#define _TRANSPORT_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * \brief Compress and encode serialized data for transport in text format. + * + * \param in Input data + * \param in_len Length of input data + * \param out If successful, contains encoded result string. + * \param out_len Must contain available bytes in \c out. Will contain length of data written to \c out if successful. + * + * \return ERR_OK if successful, ERR_FAIL if not. + */ +int pack(char *in, size_t in_len, char *out, size_t *out_len); + +/** + * + * \brief Decode and decompress data encoded in text format for transport. + * + * \param in Input data + * \param in_len Length of input data + * \param out If successful, contains encoded result string. + * \param out_len Must contain available bytes in \c out. Will contain length of data written to \c out if successful. + * + * \return ERR_OK if successful, ERR_FAIL if not. + */ +int unpack(char *in, size_t in_len, char *out, size_t *out_len); + +#ifdef __cplusplus +} +#endif + +#endif // _TRANSPORT_H diff --git a/src/ui.c b/src/ui.c @@ -0,0 +1,97 @@ +#include <gtk/gtk.h> +#include "ui.h" + +/*** + * \todo change file to resource + */ + +static void new_item(GtkListItemFactory *factory, GtkListItem *item, gpointer user_data) { +} + +static void unlock_click(GtkWidget *button, gpointer user_data) { + struct ui_container *ui; + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "click"); + + ui = (struct ui_container*)user_data; + gtk_stack_set_visible_child(ui->stack, GTK_WIDGET(ui->front_view)); +} + +GtkWidget* ui_build_unlock(struct ui_container *ui) { + GtkWidget *box; + GtkWidget *entry; + GtkWidget *button; + + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + + entry = gtk_entry_new(); + gtk_box_append(GTK_BOX(box), entry); + + button = gtk_button_new_with_label("create"); + gtk_box_append(GTK_BOX(box), button); + g_signal_connect (button, "clicked", G_CALLBACK (unlock_click), ui); + + return GTK_WIDGET(box); +} + +GtkWidget* ui_build_view(struct ui_container *ui) { + GtkSelectionModel *sel; + GtkListItemFactory *factory; + + ui->front_list = G_LIST_MODEL(gtk_string_list_new(NULL)); + sel = GTK_SELECTION_MODEL(gtk_single_selection_new(ui->front_list)); + factory = gtk_signal_list_item_factory_new(); + g_signal_connect(factory, "setup", G_CALLBACK(new_item), NULL); + ui->front_view = GTK_LIST_VIEW(gtk_list_view_new(GTK_SELECTION_MODEL(sel), factory)); + + return GTK_WIDGET(ui->front_view); +} + +void ui_build(GtkApplication *app, struct ui_container *ui) { + GtkWidget *unlock; + GtkWidget *view; + + ui->win = GTK_APPLICATION_WINDOW(gtk_application_window_new (app)); + ui->stack = GTK_STACK(gtk_stack_new()); + + gtk_window_set_title (GTK_WINDOW (ui->win), "kee"); + gtk_window_set_default_size (GTK_WINDOW (ui->win), 800, 600); + + gtk_window_set_child(GTK_WINDOW(ui->win), GTK_WIDGET(ui->stack)); + gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(ui->win), TRUE); + + unlock = ui_build_unlock(ui); + gtk_stack_add_child(ui->stack, unlock); + view = ui_build_view(ui); + gtk_stack_add_child(ui->stack, view); + + gtk_stack_set_visible_child(GTK_STACK(ui->stack), unlock); + //gtk_stack_set_visible_child(GTK_STACK(ui->stack), view); + + gtk_window_present(GTK_WINDOW (ui->win)); +} + +//void ui_build_from_resource(GtkApplication *app, struct ui_container *ui) { +// GtkBuilder *build; +// GtkWidget *unlock; +// +// build = gtk_builder_new_from_resource("/org/defalsify/Kee/main.ui"); +// //ui->view = GTK_WINDOW(gtk_builder_get_object(build, "keechoose")); +// unlock = GTK_WINDOW(gtk_builder_get_object(build, "keeunlock")); +// if (!unlock) { +// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "unlock widget could not load"); +// } +// +// ui->win = GTK_APPLICATION_WINDOW(gtk_application_window_new (app)); +// gtk_window_set_child(GTK_WINDOW(ui->win), GTK_WIDGET(unlock)); +// +// gtk_window_present(GTK_WINDOW (ui->win)); +//} +// + + +void ui_free(struct ui_container *ui) { + //g_object_unref(ui->view); + //g_object_unref(ui->unlock); + //g_object_unref(ui->win); +} diff --git a/src/ui.h b/src/ui.h @@ -0,0 +1,16 @@ +#ifndef _UI_H +#define _UI_H + +#include <gtk/gtk.h> + +struct ui_container { + GtkApplicationWindow *win; + GtkStack *stack; + GListModel *front_list; + GtkListView *front_view; +}; + +void ui_build(GtkApplication *app, struct ui_container *ui); +void ui_free(struct ui_container *ui); + +#endif // _UI_H diff --git a/testdata.py b/testdata.py @@ -0,0 +1,56 @@ +import lmdb +import tempfile +import logging +import os +import shutil +import hashlib + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +d = os.path.dirname(__file__) +d = os.path.join(d, 'testdata_mdb') +logg.info('using ' + d) + +try: + shutil.rmtree(d) +except FileNotFoundError: + pass +os.makedirs(d) +env = lmdb.open(d) +dbi = env.open_db() + +#pfx = b'\x00\x00\x00' +#pfx_two = b'\x00\x00\x01' + +keys = [] +vals = [ + b"\x03foo\x03bar", + b"\x05xyzzy\x05plugh", + b"\x04inky\x05pinky", + b"\x06blinky\x05clyde", + ] + +for i in range(len(vals)): + k = b'\x01' + i.to_bytes(16, 'big') + #k += os.urandom(32) + h = hashlib.sha256() + h.update(vals[i]) + k += h.digest() + keys.append(k) + +with env.begin(write=True) as tx: + for i in range(len(vals)): + v = i.to_bytes(2, byteorder='big') + tx.put(keys[i], vals[i]) + #tx.put(keys[i], vals[i].encode('utf-8')) + #tx.put(pfx + v, vals[i].encode('utf-8')) + #tx.put(pfx_two + b'\x00\x00', b'xyzzy') + #tx.put(pfx_two + b'\x00\x01', b'plugh') + +with env.begin() as tx: + c = tx.cursor() + #if not c.set_range(b'\x00\x00\x01'): + # raise ValueError("no key") + for k, v in c: + logg.debug('have {} {}'.format(k, v))