commit 3dd7f20a73f39046f2129eddff4cbb91a6635450
parent 26217dcc14363d8118d157a6a27691094c9a1546
Author: lash <dev@holbrook.no>
Date: Fri, 19 Apr 2024 14:34:59 +0100
WIP add signature verify in entry deserialize
Diffstat:
7 files changed, 586 insertions(+), 5 deletions(-)
diff --git a/Makefile b/Makefile
@@ -23,7 +23,7 @@ debug: gtk all
G_DEBUG=all G_MESSAGES_DEBUG=all gdb ./src/gtk/a.out
#test: gtk all test_src test_gtk
-test: testdata test_src test_gtk
+test: test_src test_gtk
testdata_schema:
asn1ate src/asn1/schema_entry.txt > testdata_asn1schema.py
@@ -31,7 +31,7 @@ testdata_schema:
test_src: all
make -C src/tests test
-test_gtk: gtk all
+test_gtk: testdata gtk all
make -C src/gtk/tests test
testdata: testdata_schema
diff --git a/src/hex.c b/src/hex.c
@@ -1,8 +1,51 @@
+#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include "hex.h"
+// cheekily stolen from https://nachtimwald.com/2017/09/24/hex-encode-and-decode-in-c/
+int hexchr2bin(const char hex, char *out) {
+ if (out == NULL)
+ return 0;
+
+ if (hex >= '0' && hex <= '9') {
+ *out = hex - '0';
+ } else if (hex >= 'A' && hex <= 'F') {
+ *out = hex - 'A' + 10;
+ } else if (hex >= 'a' && hex <= 'f') {
+ *out = hex - 'a' + 10;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+size_t hex2bin(const char *hex, unsigned char *out) {
+ size_t len;
+ char b1;
+ char b2;
+ size_t i;
+
+ if (hex == NULL || *hex == '\0' || out == NULL)
+ return 0;
+
+ len = strlen(hex);
+ if (len % 2 != 0)
+ return 0;
+ len /= 2;
+
+ memset(out, 'A', len);
+ for (i=0; i<len; i++) {
+ if (!hexchr2bin(hex[i*2], &b1) || !hexchr2bin(hex[i*2+1], &b2)) {
+ return 0;
+ }
+ //(*out)[i] = (b1 << 4) | b2;
+ *(out+i) = (b1 << 4) | b2;
+ }
+ return len;
+}
int bin_to_hex(const unsigned char *data, size_t l, unsigned char *zHex, size_t *z) {
unsigned int i;
diff --git a/src/hex.h b/src/hex.h
@@ -16,6 +16,7 @@ extern "C" {
* \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);
+size_t hex2bin(const char *hex, unsigned char *out);
#ifdef __cplusplus
}
diff --git a/src/ledger.c b/src/ledger.c
@@ -0,0 +1,464 @@
+#include <stddef.h>
+#include <libtasn1.h>
+#include <gcrypt.h>
+
+//#include "cmime.h"
+
+#include "ledger.h"
+#include "cadiz.h"
+#include "err.h"
+#include "debug.h"
+#include "digest.h"
+#include "strip.h"
+
+
+extern const asn1_static_node schema_entry_asn1_tab[];
+
+int kee_ledger_resolve(struct kee_ledger_t *ledger, struct Cadiz *resolver) {
+ return ERR_OK;
+}
+//
+// if (o->resolver) {
+// r = cadiz_resolve(o->resolver, o->body, o->body, &out_len);
+// if (!r) {
+// msg = cmime_message_new();
+// o->subject = o->body + out_len;
+// r = cmime_message_from_string(&msg, o->body, 0);
+// if (!r) {
+// o->subject = cmime_message_get_subject(msg);
+// o->subject = cmime_string_strip(o->subject);
+// out_len += strlen(o->subject) + 1;
+// }
+// remaining -= out_len;
+// } else {
+// remaining -= c;
+// }
+// }
+
+
+static char *get_message(asn1_node item, char *out_digest, char *out_data, size_t *out_len) {
+ int r;
+ size_t c;
+ asn1_node root;
+ char err[1024];
+ char buf[64];
+ char sig[64];
+
+ memset(&root, 0, sizeof(root));
+ r = asn1_array2tree(schema_entry_asn1_tab, &root, err);
+ if (r != ASN1_SUCCESS) {
+ debug_log(DEBUG_ERROR, err);
+ return NULL;
+ }
+
+ /// \todo there must be a better way to do this
+ r = asn1_copy_node(root, "Kee.KeeEntry.parent", item, "parent");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ r = asn1_copy_node(root, "Kee.KeeEntry.signatureRequest", item, "signatureRequest");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+ r = asn1_copy_node(root, "Kee.KeeEntry.response", item, "response");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+//
+// r = asn1_copy_node(root, "Kee.KeeEntry.signatureResponse", item, "signatureResponse");
+// if (r != ASN1_SUCCESS) {
+// printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+// return NULL;
+// }
+
+ r = asn1_copy_node(root, "Kee.KeeEntry.timestamp", item, "timestamp");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ r = asn1_copy_node(root, "Kee.KeeEntry.creditDelta", item, "creditDelta");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ r = asn1_copy_node(root, "Kee.KeeEntry.collateralDelta", item, "collateralDelta");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ r = asn1_copy_node(root, "Kee.KeeEntry.body", item, "body");
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ c = 64;
+ r = asn1_read_value(item, "signatureResponse", sig, (int*)&c);
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ buf[0] = 0;
+ c = 1;
+ r = asn1_write_value(root, "Kee.KeeEntry.signatureResponse", buf, c);
+ if (r != ASN1_SUCCESS) {
+ return NULL;
+ }
+
+ r = asn1_der_coding(root, "Kee.KeeEntry", out_data, (int*)out_len, err);
+ if (r != ASN1_SUCCESS) {
+ printf("%d (%s) %s\n", r, err, asn1_strerror(r));
+ return NULL;
+ }
+
+ r = calculate_digest_algo(out_data, *out_len, out_digest, GCRY_MD_SHA512);
+ if (r) {
+ return NULL;
+ }
+
+ return out_digest;
+}
+
+
+static int verify_item(asn1_node item, const char *pubkey_first_data, const char *pubkey_last_data) {
+ int r;
+ gcry_sexp_t sig;
+ gcry_sexp_t msg;
+ gcry_sexp_t pubkey;
+ gcry_error_t err;
+ size_t c;
+ gcry_mpi_t sr;
+ gcry_mpi_t ss;
+ char sig_data[64];
+ char msg_data[1024 + 64];
+ char *p = (char*)msg_data;
+ char pubkey_sexp_data[1024];
+ size_t pubkey_sexp_len;
+
+ c = 1024;
+ p = get_message(item, p, p+64, &c);
+ if (p == NULL) {
+ return 1;
+ }
+
+ c = 64;
+ r = asn1_read_value(item, "signatureResponse", sig_data, (int*)&c);
+ if (r != ASN1_SUCCESS) {
+ return 1;
+ }
+
+ c = 0;
+ err = gcry_mpi_scan(&sr, GCRYMPI_FMT_STD, sig_data, 32, &c);
+ if (err != GPG_ERR_NO_ERROR) {
+ return 1;
+ }
+ if (c != 32) {
+ return 1;
+ }
+
+ c = 0;
+ err = gcry_mpi_scan(&ss, GCRYMPI_FMT_STD, sig_data+32, 32, &c);
+ if (err != GPG_ERR_NO_ERROR) {
+ return 1;
+ }
+ if (c != 32) {
+ return 1;
+ }
+
+ c = 0;
+ err = gcry_sexp_build(&sig, &c, "(sig-val(eddsa(r %m)(s %m)))", sr, ss);
+ if (err != GPG_ERR_NO_ERROR) {
+ return 1;
+ }
+
+ c = 0;
+ err = gcry_sexp_build(&msg, &c, "(data(flags eddsa)(hash-algo sha512)(value %b))", 64, p);
+ if (err != GPG_ERR_NO_ERROR) {
+ return 1;
+ }
+
+/// \todo "string too long" error when build same string as can "new" - bug in gcrypt?
+// c = 0;
+// err = gcry_sexp_build(&pubkey, &c, "(8:key-data(10:public-key(3:ecc(5:curve7:Ed25519)(1:q32:%b))))", 32, pubkey_last_data);
+// if (err != GPG_ERR_NO_ERROR) {
+// return 1;
+// }
+
+ strcpy(pubkey_sexp_data, "(8:key-data(10:public-key(3:ecc(5:curve7:Ed25519)(1:q32:");
+ c = strlen(pubkey_sexp_data);
+ memcpy(pubkey_sexp_data + c, pubkey_last_data, 32);
+ strcpy(pubkey_sexp_data + c + 32, "))))");
+
+ pubkey_sexp_len = c + 32 + 4;
+ c = 0;
+ err = gcry_sexp_new(&pubkey, pubkey_sexp_data, pubkey_sexp_len, 1);
+ if (err != GPG_ERR_NO_ERROR) {
+ return 1;
+ }
+
+ return 0;
+}
+
+struct kee_ledger_item_t *kee_ledger_parse_item(struct kee_ledger_t *ledger, const char *data, size_t data_len) {
+ int r;
+ int c;
+ char err[1024];
+ asn1_node root;
+ asn1_node item;
+ struct kee_ledger_item_t *prev;
+ struct kee_ledger_item_t *cur;
+ int *credit_delta;
+ int *collateral_delta;
+ const char *pubkey_first;
+ const char *pubkey_last;
+ int v;
+
+
+ prev = ledger->last_item;
+ ledger->last_item = calloc(sizeof(struct kee_ledger_item_t), 1);
+ cur = ledger->last_item;
+ cur->prev_item = prev;
+
+ memset(&root, 0, sizeof(root));
+ memset(&item, 0, sizeof(item));
+ r = asn1_array2tree(schema_entry_asn1_tab, &root, err);
+ if (r != ASN1_SUCCESS) {
+ debug_log(DEBUG_ERROR, err);
+ return NULL;
+ }
+
+ r = asn1_create_element(root, "Kee.KeeEntry", &item);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return NULL;
+ }
+
+ c = (int)data_len - 1;
+ if (*(data+c)) {
+ cur->initiator = BOB;
+ credit_delta = &cur->bob_credit_delta;
+ collateral_delta = &cur->bob_collateral_delta;
+ pubkey_first = (const char*)ledger->pubkey_alice; // alice countersigns bobs
+ pubkey_last = (const char*)ledger->pubkey_bob; // alice countersigns bobs
+ } else {
+ credit_delta = &cur->alice_credit_delta;
+ collateral_delta = &cur->alice_collateral_delta;
+ pubkey_first = (const char*)ledger->pubkey_bob;
+ pubkey_last = (const char*)ledger->pubkey_alice;
+ }
+
+ r = asn1_der_decoding(&item, data, c, err);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return NULL;
+ }
+
+ r = verify_item(item, pubkey_first, pubkey_last);
+ if (r) {
+ return NULL;
+ }
+
+ c = sizeof(v);
+ r = asn1_read_value(item, "creditDelta", &v, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return NULL;
+ }
+
+ strap_be((char*)&v, c, (char*)credit_delta, sizeof(v));
+ if (is_le()) {
+ flip_endian(sizeof(v), (void*)credit_delta);
+ }
+
+ c = sizeof(v);
+ r = asn1_read_value(item, "collateralDelta", &v, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return NULL;
+ }
+
+ strap_be((char*)&v, c, (char*)collateral_delta, sizeof(v));
+ if (is_le()) {
+ flip_endian(sizeof(v), (void*)collateral_delta);
+ }
+
+ return cur;
+}
+
+void kee_ledger_item_free(struct kee_ledger_item_t *item) {
+ if (item->prev_item != NULL) {
+ kee_ledger_item_free(item->prev_item);
+ }
+ free(item);
+}
+
+void kee_ledger_free(struct kee_ledger_t *ledger) {
+ kee_ledger_item_free(ledger->last_item);
+}
+
+int kee_ledger_parse(struct kee_ledger_t *ledger, const char *data, size_t data_len) {
+ int r;
+ char err[1024];
+ asn1_node root;
+ asn1_node item;
+ int c;
+ //CMimeMessage_T *msg;
+
+ memset(ledger, 0, sizeof(struct kee_ledger_t));
+ memset(&root, 0, sizeof(root));
+ memset(&item, 0, sizeof(item));
+ r = asn1_array2tree(schema_entry_asn1_tab, &root, err);
+ if (r != ASN1_SUCCESS) {
+ debug_log(DEBUG_ERROR, err);
+ return ERR_FAIL;
+ }
+
+ r = asn1_create_element(root, "Kee.KeeEntryHead", &item);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+ c = (int)data_len;
+ r = asn1_der_decoding(&item, data, c, err);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+// r = calculate_digest_algo(data, data_len, o->current_id, GCRY_MD_SHA512);
+// if (r) {
+// return ERR_DIGESTFAIL;
+// }
+
+ c = 64;
+ r = asn1_read_value(item, "uoa", ledger->uoa, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+ c = 1;
+ r = asn1_read_value(item, "uoaDecimals", &ledger->uoa_decimals, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+ c = 32;
+ r = asn1_read_value(item, "alicePubKey", ledger->pubkey_alice, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+ c = 32;
+ r = asn1_read_value(item, "bobPubKey", ledger->pubkey_bob, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+ c = 4096 - 64;
+ r = asn1_read_value(item, "body", ledger->body, &c);
+ if (r != ASN1_SUCCESS) {
+ fprintf(stderr, "%s\n", err);
+ return r;
+ }
+
+ return ERR_OK;
+}
+
+//
+//int kee_ledger_parse_item(kee_ledger_item_t *item, const char *data, size_t data_len) {
+// int r;
+// char err[1024];
+// asn1_node root;
+// asn1_node item;
+// int alice;
+// int bob;
+// int credit;
+// int collateral;
+// int c;
+// char flag;
+// int v;
+// char *p;
+//
+// memset(&root, 0, sizeof(root));
+// memset(&item, 0, sizeof(item));
+// r = asn1_array2tree(schema_entry_asn1_tab, &root, err);
+// if (r != ASN1_SUCCESS) {
+// debug_log(DEBUG_ERROR, err);
+// return ERR_FAIL;
+// }
+//
+// r = asn1_create_element(root, "Kee.KeeEntry", &item);
+// if (r != ASN1_SUCCESS) {
+// fprintf(stderr, "%s\n", err);
+// return r;
+// }
+//
+// c = (int)data_len - 1;
+// r = asn1_der_decoding(&item, data, c, err);
+// if (r != ASN1_SUCCESS) {
+// fprintf(stderr, "%s\n", err);
+// return r;
+// }
+// flag = *(data+data_len-1);
+//
+//// c = 1;
+//// flags = 0;
+//// r = asn1_read_value(item, "flags", &flags, &c);
+//// if (r != ASN1_SUCCESS) {
+//// fprintf(stderr, "%s\n", err);
+//// return r;
+//// }
+//
+// credit = 0;
+// p = (char*)&v;
+// c = sizeof(v);
+// v = 0;
+// r = asn1_read_value(item, "creditDelta", p, &c);
+// if (r != ASN1_SUCCESS) {
+// fprintf(stderr, "%s\n", err);
+// return r;
+// }
+//
+// strap_be(p, c, (char*)&credit, sizeof(credit));
+// if (is_le()) {
+// flip_endian(sizeof(credit), (void*)&credit);
+// }
+//
+// collateral = 0;
+// c = sizeof(v);
+// v = 0;
+// r = asn1_read_value(item, "collateralDelta", p, &c);
+// if (r != ASN1_SUCCESS) {
+// fprintf(stderr, "%s\n", err);
+// return r;
+// }
+//
+// strap_be(p, c, (char*)&collateral, sizeof(collateral));
+// if (is_le()) {
+// flip_endian(sizeof(collateral), (void*)&collateral);
+// }
+//
+//
+// alice = 0;
+// bob = 0;
+// if (flag) { // bit string is left to right
+// bob = credit;
+// } else {
+// alice = credit;
+// }
+//
+// return ERR_OK;
+//}
diff --git a/src/ledger.h b/src/ledger.h
@@ -0,0 +1,37 @@
+#ifndef KEE_LEDGER_H_
+#define KEE_LEDGER_H_
+
+#include <time.h>
+
+enum kee_initiator {
+ ALICE,
+ BOB,
+};
+
+struct kee_ledger_item_t {
+ struct kee_ledger_item_t *prev_item;
+ int alice_credit_delta;
+ int bob_credit_delta;
+ int alice_collateral_delta;
+ int bob_collateral_delta;
+ time_t time;
+ enum kee_initiator initiator;
+ int response;
+ const char *body;
+};
+
+struct kee_ledger_t {
+ struct kee_ledger_item_t *last_item;
+ char pubkey_alice[32];
+ char pubkey_bob[32];
+ char uoa_decimals;
+ char uoa[64];
+ char body[4032];
+};
+
+struct kee_ledger_item_t *kee_ledger_parse_item(struct kee_ledger_t *ledger, const char *data, size_t data_len);
+int kee_ledger_parse(struct kee_ledger_t *ledger, const char *data, size_t data_len);
+void kee_ledger_free(struct kee_ledger_t *ledger);
+void kee_ledger_item_free(struct kee_ledger_item_t *item);
+
+#endif
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
+LIBS := `pkg-config --libs gtk4 zlib lmdb libgcrypt libxdg-basedir gstreamer-1.0` -lb64 -llash -ltasn1
LDFLAGS += $(LIBS)
all: obj_debug $(OBJS)
@@ -16,8 +16,11 @@ obj_debug:
%.o: %.c
$(CC) $(CFLAGS) $< -o test_$* debug.o $(LINKOBJS) $(LDFLAGS)
-test_run: $(wildcard test_*)
- ./$<
+#test_run: $(wildcard test_*)
+# ./$<
+test_run:
+ ./test_cadir
+ ./test_ledger
test: all test_run
diff --git a/src/tests/ledger.c b/src/tests/ledger.c
@@ -0,0 +1,33 @@
+#include <string.h>
+
+#include "ledger.h"
+#include "hex.h"
+
+const char *test_ledger_data = "30818e0c035553440201020420c042f26197b312fef5def17e8c7f978c3219f74981f8430cd3bd57116014d33904201613476c3986b1317fbd831a07665210a1c271f1d88a04e8aa804bb032b44f6b04403aba8490187f543270b48e770bb272021f8fdb22f584080fc9d4d02553f4504624122d99ffc9254cdb8a2ef3826e47f60e1e7b5ec28b635fbba1141e3f486aad";
+
+const char *test_item_data_a = "3082011d044000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020817c7aecdfb6a9cbd020212d8020219600440c333b18dfa822c3ce875de07d54d932ed631aaa7f996153a120bda81e2f0717f4f5e67547019bcd8af0ba6ecba95313bbdc5e43385670d67c24ef76879514ad6044010b17b8c2bc18d7c040f8a4927d20714b78f48f6c3be23867f0ba4aac96b61081f86157942620852c2a47d12a3fb066604018d4228908cf5b0c29568fd67bf0c0101ff0440d6ea4b6501aa6c87cc67bc3297a999a53d8d756873656a0ad68ce9a3b5f9be3d974acf17f4d54d27f9ecf8e854fed32b6c672fa1378142f8898ebd9d68a8540f00";
+
+
+int main() {
+ int r;
+ size_t c;
+ struct kee_ledger_t ledger;
+ struct kee_ledger_item_t *ledger_item_a;
+ char data[1024];
+
+ c = hex2bin(test_ledger_data, (unsigned char*)data);
+ r = kee_ledger_parse(&ledger, data, c);
+ if (r) {
+ return 1;
+ }
+
+ c = hex2bin(test_item_data_a, (unsigned char*)data);
+ ledger_item_a = kee_ledger_parse_item(&ledger, data, c);
+ if (ledger_item_a == NULL) {
+ return 1;
+ }
+
+ kee_ledger_free(&ledger);
+
+ return 0;
+}