kee

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

commit 8fd4617d0ee6399ca30e97bc1a73244c87137af4
parent 696fba72d238d361b65997b495c9c29af1bdd582
Author: lash <dev@holbrook.no>
Date:   Tue, 23 Apr 2024 17:19:04 +0100

Add serialization for ledger and ledgeritem

Diffstat:
Msrc/gpg.c | 48++++++++++++++++++++++++++++++------------------
Msrc/gpg.h | 2++
Msrc/ledger.c | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ledger.h | 16++++++++++++++--
Msrc/tests/Makefile | 1+
Asrc/tests/sign.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 370 insertions(+), 20 deletions(-)

diff --git a/src/gpg.c b/src/gpg.c @@ -217,34 +217,26 @@ static int key_from_path(gcry_sexp_t *key, const char *p, const char *passphrase * \todo implement MAC * \todo test new data length location (part of ciphertext) */ -static int key_create(gcry_sexp_t *key, const char *p, const char *passphrase) { +static int key_create_file(gcry_sexp_t *key, const char *p, const char *passphrase) { int r; - FILE *f; - const char *sexp_quick = "(genkey(ecc(curve Ed25519)))"; - //char *pv; + int kl; + char v[BUFLEN]; int i; int l; - int kl; size_t c; - gcry_sexp_t in; - gcry_error_t e; - char v[BUFLEN]; + FILE *f; char nonce[CHACHA20_NONCE_LENGTH_BYTES]; - e = gcry_sexp_new(&in, (const void*)sexp_quick, strlen(sexp_quick), 0); - if (e) { - printf("error sexp: %s\n", gcry_strerror(e)); - return (int)e; - } - e = gcry_pk_genkey(key, in); - if (e) { - printf("error gen: %s\n", gcry_strerror(e)); - return (int)e; + r = gpg_key_create(key); + if (r) { + return ERR_KEYFAIL; } + kl = gcry_sexp_sprint(*key, GCRYSEXP_FMT_CANON, v+sizeof(int), BUFLEN); memcpy(v, &kl, sizeof(int)); c = get_padsize(kl, ENCRYPT_BLOCKSIZE); + /// \todo malloc char ciphertext[c]; gcry_create_nonce(nonce, CHACHA20_NONCE_LENGTH_BYTES); @@ -281,6 +273,26 @@ static int key_create(gcry_sexp_t *key, const char *p, const char *passphrase) { return ERR_OK; } + +int gpg_key_create(gcry_sexp_t *key) { + const char *sexp_quick = "(genkey(ecc(curve Ed25519)))"; + //char *pv; + gcry_sexp_t in; + gcry_error_t e; + + e = gcry_sexp_new(&in, (const void*)sexp_quick, strlen(sexp_quick), 0); + if (e) { + printf("error sexp: %s\n", gcry_strerror(e)); + return (int)e; + } + e = gcry_pk_genkey(key, in); + if (e) { + printf("error gen: %s\n", gcry_strerror(e)); + return (int)e; + } + return 0; +} + static int sign(gcry_sexp_t *out, gcry_sexp_t *key, const char *v) { gcry_error_t e; gcry_sexp_t data; @@ -360,7 +372,7 @@ int gpg_store_check(struct gpg_store *gpg, const char *passphrase) { char pp[2048]; //sprintf(pp, "%s/key.bin", p.c_str()); sprintf(pp, "%s/key.bin", p); - r = key_create(&k, pp, passphrase_hash); + r = key_create_file(&k, pp, passphrase_hash); if (r != ERR_OK) { return r; } diff --git a/src/gpg.h b/src/gpg.h @@ -3,6 +3,7 @@ //#include <string> #include <stddef.h> +#include <gcrypt.h> #define GPG_MIN_VERSION "1.10.2" #define CHACHA20_KEY_LENGTH_BYTES 32 @@ -86,5 +87,6 @@ void gpg_store_init(struct gpg_store *gpg, const char *path); int gpg_store_check(struct gpg_store *gpg, const char *passphrase); int gpg_store_digest(struct gpg_store *gpg, char *out, const char *in); char *gpg_store_get_fingerprint(struct gpg_store *gpg); +int gpg_key_create(gcry_sexp_t *key); #endif diff --git a/src/ledger.c b/src/ledger.c @@ -10,9 +10,11 @@ #include "digest.h" #include "strip.h" #include "content.h" +#include "endian.h" extern const asn1_static_node schema_entry_asn1_tab[]; +const char zero_content[64]; static char *get_message(asn1_node item, char *out_digest, char *out_data, size_t *out_len) { int r; @@ -340,6 +342,10 @@ void kee_ledger_init(struct kee_ledger_t *ledger) { memset(ledger, 0, sizeof(struct kee_ledger_t)); } +void kee_ledger_item_init(struct kee_ledger_item_t *item) { + memset(item, 0, sizeof(struct kee_ledger_item_t)); +} + int kee_ledger_parse(struct kee_ledger_t *ledger, const char *data, size_t data_len) { int r; char err[1024]; @@ -418,3 +424,183 @@ void kee_ledger_resolve(struct kee_ledger_t *ledger, Cadiz *cadiz) { item = item->prev_item; } } + +int kee_ledger_serialize(struct kee_ledger_t *ledger, char *out, size_t *out_len) { + int r; + char err[1024]; + asn1_node node; + int c; + + //memset(&root, 0, sizeof(root)); + memset(&node, 0, sizeof(node)); + //r = asn1_array2tree(schema_entry_asn1_tab, &root, err); + r = asn1_array2tree(schema_entry_asn1_tab, &node, err); + if (r != ASN1_SUCCESS) { + return ERR_FAIL; + } + + c = strlen(ledger->uoa); + r = asn1_write_value(node, "Kee.KeeEntryHead.uoa", ledger->uoa, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 4; + r = asn1_write_value(node, "Kee.KeeEntryHead.uoaDecimals", &ledger->uoa_decimals, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 32; + r = asn1_write_value(node, "Kee.KeeEntryHead.alicePubKey", ledger->pubkey_alice, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 32; + r = asn1_write_value(node, "Kee.KeeEntryHead.bobPubKey", ledger->pubkey_bob, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 64; + r = asn1_write_value(node, "Kee.KeeEntryHead.body", ledger->content.key, c); + if (r != ASN1_SUCCESS) { + return r; + } + + r = asn1_der_coding(node, "Kee.KeeEntryHead", out, (int*)out_len, err); + if (r != ASN1_SUCCESS) { + return r; + } + + return 0; +} + +int kee_ledger_item_serialize(struct kee_ledger_item_t *item, char *out, size_t *out_len, enum kee_item_serialize_mode_e mode) { + int r; + char err[1024]; + asn1_node node; +// char timedata[8]; + long long nanotime; + int c; + int credit_delta; + int collateral_delta; + char *signature_request; + char *signature_response; + char *response_s; + + //memset(&root, 0, sizeof(root)); + memset(&node, 0, sizeof(node)); + //r = asn1_array2tree(schema_entry_asn1_tab, &root, err); + r = asn1_array2tree(schema_entry_asn1_tab, &node, err); + if (r != ASN1_SUCCESS) { + return ERR_FAIL; + } + + c = 64; + if (item->prev_item == NULL) { + r = asn1_write_value(node, "Kee.KeeEntry.parent", zero_content, c); + } else { + r = asn1_write_value(node, "Kee.KeeEntry.parent", item->prev_item, c); + } + if (r != ASN1_SUCCESS) { + return r; + } + +// memcpy(timedata, item->time, 4); +// r = to_endian(TO_ENDIAN_BIG, 4, timedata); +// if (r) { +// return 1; +// } +// +// memcpy(timedata+4, item->time+4, 4); +// r = to_endian(TO_ENDIAN_BIG, 4, timedata+4); +// if (r) { +// return 1; +// } + + nanotime = item->time.tv_sec * 1000000000; + nanotime += item->time.tv_nsec; + r = to_endian(TO_ENDIAN_BIG, 8, &nanotime); + if (r) { + return 1; + } + + c = 8; + r = asn1_write_value(node, "Kee.KeeEntry.timestamp", &nanotime, c); + if (r != ASN1_SUCCESS) { + return r; + } + + if (item->initiator == BOB) { + credit_delta = item->bob_credit_delta; + collateral_delta = item->bob_collateral_delta; + signature_request = item->bob_signature; + signature_response = item->alice_signature; + + } else { + credit_delta = item->alice_credit_delta; + collateral_delta = item->alice_collateral_delta; + signature_request = item->alice_signature; + signature_response = item->bob_signature; + } + c = 4; + r = asn1_write_value(node, "Kee.KeeEntry.creditDelta", &credit_delta, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 4; + r = asn1_write_value(node, "Kee.KeeEntry.collateralDelta", &collateral_delta, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 64; + r = asn1_write_value(node, "Kee.KeeEntry.body", item->content.key, c); + if (r != ASN1_SUCCESS) { + return r; + } + + if (mode == KEE_LEDGER_ITEM_SERIALIZE_REQUEST) { + signature_request = zero_content; + c = 0; + } else { + c = 64; + } + r = asn1_write_value(node, "Kee.KeeEntry.signatureRequest", signature_request, c); + if (r != ASN1_SUCCESS) { + return r; + } + + c = 5; + if (item->response) { + response_s = "TRUE"; + } else { + response_s = "FALSE"; + c++; + } + r = asn1_write_value(node, "Kee.KeeEntry.response", response_s, c); + if (r != ASN1_SUCCESS) { + return r; + } + + if (mode < KEE_LEDGER_ITEM_SERIALIZE_FINAL) { + signature_response = zero_content; + c = 0; + } else { + c = 64; + } + r = asn1_write_value(node, "Kee.KeeEntry.signatureResponse", signature_response, c); + if (r != ASN1_SUCCESS) { + return r; + } + + r = asn1_der_coding(node, "Kee.KeeEntry", out, (int*)out_len, err); + if (r != ASN1_SUCCESS) { + return r; + } + + return 0; +} diff --git a/src/ledger.h b/src/ledger.h @@ -11,15 +11,23 @@ enum kee_initiator_e { BOB, }; +enum kee_item_serialize_mode_e { + KEE_LEDGER_ITEM_SERIALIZE_REQUEST, + KEE_LEDGER_ITEM_SERIALIZE_RESPONSE, + KEE_LEDGER_ITEM_SERIALIZE_FINAL, +}; + 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; + struct timespec time; enum kee_initiator_e initiator; char response; + char alice_signature[64]; + char bob_signature[64]; struct kee_content_t content; }; @@ -32,7 +40,7 @@ struct kee_ledger_cache_t { }; struct kee_ledger_t { - const char digest[64]; + char digest[64]; struct kee_ledger_item_t *last_item; char pubkey_alice[32]; char pubkey_bob[32]; @@ -44,10 +52,14 @@ struct kee_ledger_t { 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); +int kee_ledger_serialize(struct kee_ledger_t *ledger, char *out, size_t *out_len); void kee_ledger_init(struct kee_ledger_t *ledger); void kee_ledger_free(struct kee_ledger_t *ledger); void kee_ledger_item_free(struct kee_ledger_item_t *item); void kee_ledger_resolve(struct kee_ledger_t *ledger, Cadiz *cadiz); void kee_ledger_reset_cache(struct kee_ledger_t *ledger); +void kee_ledger_item_init(struct kee_ledger_item_t *item); +int kee_ledger_item_serialize(struct kee_ledger_item_t *item, char *out, size_t *out_len, enum kee_item_serialize_mode_e mode); + #endif diff --git a/src/tests/Makefile b/src/tests/Makefile @@ -23,6 +23,7 @@ test_run: ./test_content ./test_ledger ./test_dn + ./test_sign test: all test_run diff --git a/src/tests/sign.c b/src/tests/sign.c @@ -0,0 +1,137 @@ +#include <gcrypt.h> + +#include "ledger.h" +#include "gpg.h" +#include "digest.h" + +const char *content_test = "Subject: foo\n\nsome content\n"; +const char *content_test_item = "Subject: bar\n\nsome other content\n"; +const char *content_test_item_two = "Subject: baz\n\nmore more more content\n"; + +int main() { + int r; + gcry_sexp_t alice; + gcry_sexp_t bob; + gcry_sexp_t tmp; + char *out; + size_t out_len; + char *out_item; + size_t out_item_len; + struct kee_ledger_t ledger; + struct kee_ledger_item_t item; + struct kee_ledger_item_t item_two; + struct kee_content_t content; + struct kee_content_t content_item; + struct kee_content_t content_item_two; + char item_sum[64]; + + r = gpg_key_create(&alice); + if (r) { + return 1; + } + r = gpg_key_create(&bob); + if (r) { + return 1; + } + + kee_ledger_init(&ledger); + + tmp = gcry_sexp_find_token(alice, "public-key", 10); + if (tmp == NULL) { + return 1; + } + tmp = gcry_sexp_find_token(tmp, "q", 1); + if (tmp == NULL) { + return 1; + } + out_len = 32; + out = gcry_sexp_nth_data(tmp, 1, &out_len); + if (tmp == NULL) { + return 1; + } + memcpy(ledger.pubkey_alice, out, 32); + + tmp = gcry_sexp_find_token(bob, "public-key", 10); + if (tmp == NULL) { + return 1; + } + tmp = gcry_sexp_find_token(tmp, "q", 1); + if (tmp == NULL) { + return 1; + } + out_len = 32; + out = gcry_sexp_nth_data(tmp, 1, &out_len); + if (tmp == NULL) { + return 1; + } + memcpy(ledger.pubkey_bob, out, 32); + + strcpy(ledger.uoa, "USD"); + ledger.uoa_decimals = 2; + + r = calculate_digest_algo(content_test, strlen(content_test), content.key, GCRY_MD_SHA512); + if (r) { + return 1; + } + r = kee_content_init(&content, content.key, 0); + if (r) { + return 1; + } + + r = calculate_digest_algo(content_test, strlen(content_test), content.key, GCRY_MD_SHA512); + if (r) { + return 1; + } + + out_len = 4096; + out = malloc(out_len); + r = kee_ledger_serialize(&ledger, out, &out_len); + if (r) { + return 1; + } + + r = calculate_digest_algo(out, out_len, ledger.digest, GCRY_MD_SHA512); + if (r) { + return 1; + } + + kee_ledger_item_init(&item); + item.alice_credit_delta = 666; + item.bob_credit_delta = -42; + item.alice_collateral_delta = 1024; + item.bob_collateral_delta = 2048; + r = clock_gettime(CLOCK_REALTIME, &item.time); + if (r) { + return 1; + } + item.initiator = BOB; + item.response = 1; + + r = calculate_digest_algo(content_test, strlen(content_test_item), content_item.key, GCRY_MD_SHA512); + if (r) { + return 1; + } + r = kee_content_init(&content_item, content_item.key, 0); + if (r) { + return 1; + } + + free(out); + kee_content_free(&content_item); + kee_content_free(&content); + kee_ledger_free(&ledger); + + out_item_len = 4096; + out_item = malloc(out_len); + r = kee_ledger_item_serialize(&item, out_item, &out_item_len, KEE_LEDGER_ITEM_SERIALIZE_REQUEST); + if (r) { + return 1; + } + + r = calculate_digest_algo(out_item, out_item_len, item_sum, GCRY_MD_SHA512); + if (r) { + return 1; + } + + return 0; +}