kee

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

commit 3b310df2078f8d6c5a1297908a1f24853fa70c12
parent 4c941396f33c8178398480e39f13ffaf5f544384
Author: lash <dev@holbrook.no>
Date:   Sun, 21 Apr 2024 09:21:45 +0100

Add DN to pubkey, ldap dn parse on kee entry

Diffstat:
MREADME.md | 1+
Msrc/db.h | 2++
Asrc/dn.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/dn.h | 23+++++++++++++++++++++++
Msrc/gtk/Makefile | 2+-
Msrc/gtk/kee-entry-store.c | 6+++++-
Msrc/gtk/kee-entry.c | 43+++++++++++++++++++++++++++++++++++++++++--
Msrc/ledger.c | 3+++
Msrc/tests/Makefile | 3++-
Asrc/tests/dn.c | 28++++++++++++++++++++++++++++
Mtestdata_asn1.py | 57+++++++++++++++++++++++++++++++++++++++++----------------
11 files changed, 217 insertions(+), 21 deletions(-)

diff --git a/README.md b/README.md @@ -19,6 +19,7 @@ Below are the library versions of the [archlinux](https://archlinux.org/) compon | libgcrypt | 1.10.3 | | libxdg-basedir | 1.2.3 | | lmdb | 0.9.32 | +| openldap | 2.6.7 | | rustup | 1.26.0 | | zbar | 0.23.90 | | zlib | 1.3.1 | diff --git a/src/db.h b/src/db.h @@ -29,6 +29,8 @@ enum DbKey { DbKeyLedgerHead = 0x01, /// A credit item record DbKeyLedgerEntry = 0x02, + /// A DN record + DbKeyDN = 0x03, /// A reverse lookup record; resolves the content hash to the content entry in the database. DbKeyReverse = 0xff, }; diff --git a/src/dn.c b/src/dn.c @@ -0,0 +1,70 @@ +#include <stdlib.h> +#include <string.h> + +#include <ldap.h> + +#include "err.h" +#include "dn.h" + +struct kee_dn_t* kee_dn_init(struct kee_dn_t *dn, size_t cap) { + if (cap == 0) { + cap = KEE_DN_DEFAULT_CAP; + } + dn->mem = malloc(cap); + dn->p = (char*)dn->mem; + dn->cn = NULL; + return dn; +} + +int kee_dn_from_str(struct kee_dn_t *dn, const char *s, size_t l) { + int r; + int i; + LDAPDN ldn; + LDAPRDN lrdn; + LDAPAVA *ldnav; + char tmp[1024]; + char *dst; + + memcpy(tmp, s, l); + *(tmp+l) = 0; + r = ldap_str2dn(tmp, &ldn, LDAP_DN_FORMAT_LDAPV3); + if (r) { + return ERR_FAIL; + } + + i = 0; + while(1) { + lrdn = *(ldn+i); + if (lrdn == NULL) { + break; + } + ldnav = *lrdn; + + memcpy(tmp, ldnav->la_attr.bv_val, ldnav->la_attr.bv_len); + tmp[ldnav->la_attr.bv_len] = 0; + if (!strcmp(tmp, "CN")) { + dn->cn = dn->p; + dst = dn->cn; + } else if (!strcmp(tmp, "O")) { + dn->o = dn->p; + dst = dn->o; + } else { + return 1; + } + memcpy(dst, ldnav->la_value.bv_val, ldnav->la_value.bv_len); + *(dst+ldnav->la_value.bv_len) = 0; + dn->p += ldnav->la_value.bv_len + 1; + i++; + } + if (dn->cn == NULL) { + return 1; + } + + ldap_dnfree(ldn); + + return 0; +} + +void kee_dn_free(struct kee_dn_t *dn) { + free(dn->mem); +} diff --git a/src/dn.h b/src/dn.h @@ -0,0 +1,23 @@ +#ifndef KEE_DN_H_ +#define KEE_DN_H_ + +#ifndef KEE_DN_DEFAULT_CAP +#define KEE_DN_DEFAULT_CAP 1024 +#endif + +struct kee_dn_t { + char *mem; + char *p; + char *cn; + char *c; + char *o; + char *uid; + char *dc; +}; + +struct kee_dn_t* kee_dn_init(struct kee_dn_t *dn, size_t cap); +int kee_dn_from_str(struct kee_dn_t *dn, const char *s, size_t l); +void kee_dn_free(struct kee_dn_t *dn); + +#endif + diff --git a/src/gtk/Makefile b/src/gtk/Makefile @@ -5,7 +5,7 @@ INCLUDES := -I.. -I../aux/include 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 +LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0 libtasn1` -lb64 -lcmime -llash -lldap LDFLAGS += $(LIBS) all: resource $(OBJS) diff --git a/src/gtk/kee-entry-store.c b/src/gtk/kee-entry-store.c @@ -62,6 +62,7 @@ static guint kee_entry_store_get_n_items(GListModel *list) { static gpointer kee_entry_store_get_item(GListModel *list, guint index) { + int r; KeeEntry *o; KeeEntryStore *store; @@ -71,7 +72,10 @@ static gpointer kee_entry_store_get_item(GListModel *list, guint index) { kee_entry_set_resolver(o, &store->resolver); kee_entry_store_seek(store, index); //kee_entry_deserialize(o, store->last_key, 9, store->last_value, store->last_value_length); - kee_entry_deserialize(o, store->last_value, store->last_value_length); + r = kee_entry_deserialize(o, store->last_value, store->last_value_length); + if (r) { + return NULL; + } //return o; kee_entry_apply_list_item_widget(o); diff --git a/src/gtk/kee-entry.c b/src/gtk/kee-entry.c @@ -19,6 +19,7 @@ #include "endian.h" #include "strip.h" #include "ledger.h" +#include "dn.h" typedef struct { @@ -35,7 +36,8 @@ struct _KeeEntry { GtkWidget parent; int state; char header[1024]; - char *current_id[128]; + struct kee_dn_t bob_dn; + char current_id[128]; struct kee_ledger_t ledger; struct Cadiz *resolver; struct db_ctx *db; @@ -68,6 +70,9 @@ static void kee_entry_finalize(GObject *o) { KeeEntry *entry = KEE_ENTRY(o); kee_ledger_free(&entry->ledger); + + kee_dn_free(&entry->bob_dn); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "tearing down entry"); //G_OBJECT_CLASS(kee_entry_parent_class)->finalize(o); } @@ -87,6 +92,7 @@ KeeEntry* kee_entry_new(struct db_ctx *db) { KeeEntry *o; o = KEE_ENTRY(g_object_new(KEE_TYPE_ENTRY, "orientation", GTK_ORIENTATION_VERTICAL, NULL)); o->db = db; + kee_dn_init(&o->bob_dn, 0); return o; } @@ -96,6 +102,15 @@ void kee_entry_set_resolver(KeeEntry *o, struct Cadiz *resolver) { int kee_entry_deserialize(KeeEntry *o, const char *data, size_t data_len) { int r; + size_t key_len; + size_t last_value_length; + char mem[33 + 1024]; + char *last_key; + char *last_value = mem + 33; + + last_key = mem; + key_len = 33; + last_value_length = 1024; r = kee_ledger_parse(&o->ledger, data, data_len); if (r) { @@ -103,6 +118,20 @@ int kee_entry_deserialize(KeeEntry *o, const char *data, size_t data_len) { } kee_content_resolve(&o->ledger.content, o->resolver); + last_value_length = 2048; + *last_key = DbKeyDN; + memcpy(last_key+1, o->ledger.pubkey_bob, 32); + key_len = 33; + r = db_next(o->db, DbKeyDN, &last_key, &key_len, &last_value, &last_value_length); + if (r) { + return ERR_FAIL; + } + db_rewind(o->db); + r = kee_dn_from_str(&o->bob_dn, last_value, last_value_length); + if (r) { + return ERR_FAIL; + } + r = calculate_digest_algo(data, data_len, o->current_id, GCRY_MD_SHA512); if (r) { return ERR_DIGESTFAIL; @@ -132,16 +161,24 @@ static int kee_entry_deserialize_item(KeeEntry *o, const char *data, size_t data } void kee_entry_apply_list_item_widget(KeeEntry *o) { + int r; GtkWidget *widget; size_t l; unsigned char alice_hex[129]; unsigned char bob_hex[129]; + char *bob; if (o->state) { g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "entry must be loaded first"); return; } +// bob = NULL; +// r = ldap_rdn2str(*o->bob_dn, &bob, LDAP_DN_FORMAT_LDAPV3); +// if (r) { +// return; +// } + l = 129; bin_to_hex((unsigned char*)o->ledger.pubkey_alice, 64, alice_hex, &l); l = 129; @@ -149,7 +186,7 @@ void kee_entry_apply_list_item_widget(KeeEntry *o) { sprintf(o->header, "[%s] %s -> %s", o->ledger.uoa, alice_hex, bob_hex); widget = gtk_label_new(o->header); gtk_box_append(GTK_BOX(o), widget); - widget = gtk_label_new(o->ledger.content.subject); + widget = gtk_label_new(o->bob_dn.cn); gtk_box_append(GTK_BOX(o), widget); return; } @@ -226,6 +263,8 @@ void kee_entry_apply_entry(KeeEntry *target, KeeEntry *orig) { memcpy(target->current_id, orig->current_id, 128); target->resolver = orig->resolver; target->ledger = orig->ledger; + target->state = orig->state; + target->bob_dn = orig->bob_dn; return; } diff --git a/src/ledger.c b/src/ledger.c @@ -299,6 +299,9 @@ struct kee_ledger_item_t *kee_ledger_parse_item(struct kee_ledger_t *ledger, con } void kee_ledger_item_free(struct kee_ledger_item_t *item) { + if (item == NULL) { + return; + } if (item->prev_item != NULL) { kee_ledger_item_free(item->prev_item); } diff --git a/src/tests/Makefile b/src/tests/Makefile @@ -5,7 +5,7 @@ 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 +LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0` -lb64 -llash -ltasn1 -lcmime -lldap LDFLAGS += $(LIBS) all: obj_debug $(OBJS) @@ -22,6 +22,7 @@ test_run: ./test_cadir ./test_content ./test_ledger + ./test_dn test: all test_run diff --git a/src/tests/dn.c b/src/tests/dn.c @@ -0,0 +1,28 @@ +#include <string.h> +#include <stddef.h> + +#include "dn.h" + +int main() { + int r; + struct kee_dn_t dn; + + kee_dn_init(&dn, 0); + + r = kee_dn_from_str(&dn, "CN=Foo Bar,O=Baz", 16); + if (r) { + return 1; + } + + if (strcmp(dn.cn, "Foo Bar")) { + return 1; + } + + if (strcmp(dn.o, "Baz")) { + return 1; + } + + kee_dn_free(&dn); + + return 0; +} diff --git a/testdata_asn1.py b/testdata_asn1.py @@ -35,7 +35,7 @@ NOBODY = b'\x00' * 64 NOSIG = b'' PFX_LEDGER_HEAD = b'\x01' PFX_LEDGER_ENTRY = b'\x02' -PFX_LEDGER_COUNTERKEY = b'\x03' +PFX_LEDGER_PUBKEY = b'\x03' random.seed(int(time.time_ns())) @@ -89,9 +89,18 @@ class LedgerSigner: self.signer = {} self.keypair = {} self.pubkey_rindex = {} + self.names = {} self.crypto_dir = crypto_dir + def get_pubkey(self, k): + return self.keypair[k][1] + + + def get_name(self, k): + return self.names[k] + + def __write_key(self, keyname, outdir, pin): (pk, pubk) = self.keypair[keyname] wt = io.BytesIO() @@ -132,7 +141,7 @@ class LedgerSigner: w = open(fp, "wb") w.write(b) w.close() - + def create_key(self, keyname, outdir=None, pin='1234'): k = ECC.generate(curve='Ed25519') @@ -141,6 +150,7 @@ class LedgerSigner: pk = Crypto.Util.asn1.DerOctetString().decode(pk_der[1], strict=True).payload pubk = k.public_key().export_key(format='raw') + self.signer[keyname] = eddsa.new(k, 'rfc8032') self.keypair[keyname] = (pk, pubk) self.pubkey_rindex[pubk] = keyname @@ -150,6 +160,8 @@ class LedgerSigner: self.__write_key(keyname, outdir, pin) + self.names[keyname] = fake.name() + return pubk @@ -303,7 +315,7 @@ class LedgerEntry(Ledger): collateral_bob = 0 ms = 0 - def __init__(self, head, signer, generator, parent=None, body=NOBODY): + def __init__(self, head, signer, generator, parent=None, body=NOBODY, bob_name='bob'): self.head = head self.parent = parent if self.parent == None: @@ -315,9 +327,9 @@ class LedgerEntry(Ledger): delta = generator.delta() self.signer_sequence = [] if delta[2]: - self.signer_sequence = ['bob', 'alice'] + self.signer_sequence = [bob_name, 'alice'] else: - self.signer_sequence = ['alice', 'bob'] + self.signer_sequence = ['alice', bob_name] self.credit_delta = delta[0] self.collateral_delta = delta[1] @@ -378,8 +390,8 @@ class LedgerEntry(Ledger): return r -def generate_entry(data_dir, signer, generator, head, parent): - o = LedgerEntry(head, signer, generator, parent=parent) +def generate_entry(data_dir, signer, generator, head, bob_name, parent): + o = LedgerEntry(head, signer, generator, parent=parent, bob_name=bob_name) w = io.BytesIO() r = o.serialize(data_dir, w=w) h = hashlib.new('sha512') @@ -389,7 +401,7 @@ def generate_entry(data_dir, signer, generator, head, parent): return (z, b,) -def generate_ledger(data_dir, signer, entry_count=3, alice=None, bob=None): +def generate_ledger(dbi, data_dir, signer, bob_name, entry_count=3, alice=None, bob=None): r = [] o = LedgerHead(alice_key=alice, bob_key=bob) w = io.BytesIO() @@ -408,7 +420,7 @@ def generate_ledger(data_dir, signer, entry_count=3, alice=None, bob=None): generator = LedgerGenerator() for i in range(entry_count): try: - v = generate_entry(data_dir, signer, generator, k, parent=parent) + v = generate_entry(data_dir, signer, generator, k, bob_name, parent) except OverflowError: break # \todo generate key value already here @@ -437,22 +449,29 @@ if __name__ == '__main__': d = db_init(d) - f = open('key.bin', 'wb') + env = lmdb.open(d) + dbi = env.open_db() + signer = LedgerSigner(crypto_dir) - alice = signer.create_key('alice', data_dir) - f.close() + alice = signer.create_key('alice', outdir=data_dir) #bob = bob(d) - bob = signer.create_key('bob', data_dir) - env = lmdb.open(d) - dbi = env.open_db() + keys = ['alice'] + + alice_key = os.path.join(crypto_dir, 'alice.key.bin') + os.unlink('key.bin') + os.symlink(alice_key, 'key.bin') count_ledgers = os.environ.get('COUNT', '1') with env.begin(write=True) as tx: for i in range(int(count_ledgers)): + bob_name = 'Bob ' + fake.last_name() + keys.append(bob_name) + bob = signer.create_key(bob_name, outdir=data_dir) + c = random.randint(2, 20) - r = generate_ledger(data_dir, signer, entry_count=c, alice=alice, bob=bob) + r = generate_ledger(dbi, data_dir, signer, bob_name, entry_count=c, alice=alice, bob=bob) v = r.pop(0) @@ -463,3 +482,9 @@ if __name__ == '__main__': for v in r: k = LedgerEntry.to_key(v[1], z) tx.put(k, v[1]) + + for k in keys: + pubk = signer.get_pubkey(k) + name = signer.get_name(k).encode('utf-8') + tx.put(PFX_LEDGER_PUBKEY + pubk, b'CN=' + name) +