kee

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

commit 8a228e775f432e750b8a54f49881c97608ba644e
parent 802d1a2be68a8f417cbedb0e86f424309495adad
Author: lash <dev@holbrook.no>
Date:   Thu, 25 Apr 2024 21:56:58 +0100

WIP very close to store refactor and complete sign and serialize verify tests

Diffstat:
Msrc/err.h | 2++
Msrc/gpg.c | 365++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/gpg.h | 23++++++++++++++++++++---
Msrc/gtk/kee-entry-item.c | 29+++++++++++++++++++++++++++++
Msrc/gtk/kee-entry-item.h | 9+++++++++
Msrc/gtk/kee-entry.c | 29+++++++++++++++++++++++++----
Msrc/ledger.c | 168++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/ledger.h | 1+
Msrc/tests/ledger.c | 6+++---
Msrc/tests/sign.c | 214+++++++++++++++++++++++++++++--------------------------------------------------
Mtestdata_asn1.py | 4++--
11 files changed, 617 insertions(+), 233 deletions(-)

diff --git a/src/err.h b/src/err.h @@ -27,6 +27,8 @@ enum keeError { ERR_NOKEY, /// Crypto authentication fail ERR_KEYFAIL, + /// + ERR_ALREADY_SIGNED, }; #endif // _KEE_ERR_H diff --git a/src/gpg.c b/src/gpg.c @@ -3,6 +3,8 @@ #include <stdio.h> #include <stddef.h> #include <errno.h> +#include <unistd.h> +#include <errno.h> #include "err.h" #include "debug.h" @@ -157,6 +159,45 @@ int decrypt(char *outdata, const char *ciphertext, size_t ciphertext_len, const return ERR_OK; } +static int key_apply_public(struct gpg_store *gpg, gcry_sexp_t key) { + char *p; + size_t c; + gcry_sexp_t pubkey; + + pubkey = gcry_sexp_find_token(key, "public-key", 10); + if (pubkey == NULL) { + return 1; + } + pubkey = gcry_sexp_find_token(pubkey, "q", 1); + if (pubkey == NULL) { + return 1; + } + c = PUBKEY_LENGTH; + p = (char*)gcry_sexp_nth_data(pubkey, 1, &c); + if (p == NULL) { + return 1; + } + memcpy(gpg->public_key, p, PUBKEY_LENGTH); + return 0; +} + +static char *key_filename(struct gpg_store *gpg, char *path) { + int r; + char *p; + size_t c; + + strcpy((char*)path, gpg->path); + p = (char*)path + strlen((char*)path); + + c = 41; + r = bin_to_hex((unsigned char*)gpg->fingerprint, 20, (unsigned char*)p, &c); + if (r) { + return NULL; + } + + return path; +} + static int key_from_data(gcry_sexp_t *key, const char *indata, size_t indata_len) { gcry_error_t e; @@ -168,24 +209,20 @@ static int key_from_data(gcry_sexp_t *key, const char *indata, size_t indata_len return ERR_OK; } -static int key_from_path(gcry_sexp_t *key, const char *p, const char *passphrase) { +static int key_from_file(gcry_sexp_t *key, const char *path, const char *passphrase) { + char *p; int r; char v[BUFLEN]; size_t c; size_t i; FILE *f; - char *fullpath; char nonce[CHACHA20_NONCE_LENGTH_BYTES]; void *outdata; - fullpath = (char*)malloc(strlen(p) + 8); - sprintf(fullpath, "%s/%s", p, "key.bin"); - - f = fopen(fullpath, "r"); + f = fopen(path, "r"); if (f == NULL) { return ERR_NOKEY; } - free(fullpath); /// \todo length must be in the ciphertext //c = fread(&l, sizeof(int), 1, f); @@ -208,45 +245,99 @@ static int key_from_path(gcry_sexp_t *key, const char *p, const char *passphrase return ERR_NOKEY; } //r = key_from_data(key, (char*)outdata, l); - r = key_from_data(key, (char*)(outdata+sizeof(int)), (size_t)(*((int*)outdata))); + c = (size_t)(*((int*)outdata)); + p = (char*)(outdata+sizeof(int)); + r = key_from_data(key, p, c); free(outdata); return r; } +static int key_create(struct gpg_store *gpg, gcry_sexp_t *key) { + int r; + char *p; + const char *sexp_quick = "(genkey(ecc(flags eddsa)(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; + } + p = (char*)gcry_pk_get_keygrip(*key, (unsigned char*)gpg->fingerprint); + if (p == NULL) { + return ERR_KEYFAIL; + } + + r = key_apply_public(gpg, *key); + if (r) { + return ERR_KEYFAIL; + } + + return 0; +} + /** * \todo consistent endianness for key length in persistent storage (fwrite) - * \todo implement MAC + et* \todo implement MAC * \todo test new data length location (part of ciphertext) + * \todo doc must have enough in path for path + fingerprint hex + * */ -static int key_create_file(gcry_sexp_t *key, const char *p, const char *passphrase) { +static int key_create_file(struct gpg_store *gpg, gcry_sexp_t *key, const char *passphrase) { + char *p; int r; int kl; char v[BUFLEN]; int i; int l; size_t c; + size_t m; FILE *f; char nonce[CHACHA20_NONCE_LENGTH_BYTES]; + char path[1024]; - r = gpg_key_create(key); + r = key_create(gpg, key); if (r) { return ERR_KEYFAIL; } - kl = gcry_sexp_sprint(*key, GCRYSEXP_FMT_CANON, v+sizeof(int), BUFLEN); - memcpy(v, &kl, sizeof(int)); + kl = gcry_sexp_sprint(*key, GCRYSEXP_FMT_CANON, NULL, 0); + m = (size_t)kl; + p = (char*)v + sizeof(int); + c = 0; + while (m > 0) { + kl = gcry_sexp_sprint(*key, GCRYSEXP_FMT_CANON, p, BUFLEN-m); + m -= (size_t)(kl + 1); + p += kl; + c += kl; + } + memcpy(v, &c, sizeof(int)); - c = get_padsize(kl, ENCRYPT_BLOCKSIZE); + m = c; + c = get_padsize(m, ENCRYPT_BLOCKSIZE); /// \todo malloc char ciphertext[c]; gcry_create_nonce(nonce, CHACHA20_NONCE_LENGTH_BYTES); - r = encryptb(ciphertext, c, v, kl, passphrase, nonce); + r = encryptb(ciphertext, c, v, m+sizeof(int), passphrase, nonce); if (r) { return ERR_KEYFAIL; } - f = fopen(p, "w"); + + p = key_filename(gpg, path); + if (p == NULL) { + return ERR_KEYFAIL; + } + + f = fopen((char*)path, "w"); if (f == NULL) { return ERR_KEYFAIL; } @@ -275,26 +366,87 @@ static int key_create_file(gcry_sexp_t *key, const char *p, const char *passphra } -int gpg_key_create(gcry_sexp_t *key) { - const char *sexp_quick = "(genkey(ecc(flags eddsa)(curve Ed25519)))"; - //char *pv; - gcry_sexp_t in; - gcry_error_t e; +int gpg_key_create(struct gpg_store *gpg, const char *passphrase) { + size_t c; + char *p; + int r; + gcry_sexp_t key; + char key_path[1024]; + char link_path[1024]; - 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; + r = key_create_file(gpg, &key, passphrase); + if (r) { + return ERR_KEYFAIL; } - e = gcry_pk_genkey(key, in); - if (e) { - printf("error gen: %s\n", gcry_strerror(e)); - return (int)e; + + p = key_filename(gpg, key_path); + if (p == NULL) { + return ERR_KEYFAIL; } - return 0; + + strcpy(link_path, gpg->path); + c = strlen(link_path); + strcpy(link_path + c, "kee.key"); + + r = unlink(link_path); + if (r == -1 && errno != ENOENT) { + return ERR_KEYFAIL; + } + + r = symlink(key_path, link_path); + if (r) { + return ERR_KEYFAIL; + } + return ERR_OK; +} + +int gpg_key_load(struct gpg_store *gpg, const char *passphrase, enum gpg_find_mode_e mode, void *criteria) { + int r; + size_t c; + char *p; + char path[1024]; + + switch(mode) { + case KEE_GPG_FIND_MAIN: + strcpy(path, gpg->path); + p = path + strlen(path); + strcpy(p, "kee.key"); + r = key_from_file(gpg->k, path, passphrase); + if (r) { + return ERR_FAIL; + } + break; + case KEE_GPG_FIND_FINGERPRINT: + strcpy(path, gpg->path); + p = path + strlen(path); + c = 41; + r = bin_to_hex((const unsigned char*)criteria, FINGERPRINT_LENGTH, (unsigned char*)p, &c); + if (r) { + return ERR_FAIL; + } + r = key_from_file(gpg->k, path, passphrase); + if (r) { + return ERR_FAIL; + } + break; + default: + return ERR_FAIL; + } + + p = (char*)gcry_pk_get_keygrip(*gpg->k, (unsigned char*)gpg->fingerprint); + if (p == NULL) { + return ERR_KEYFAIL; + } + + r = key_apply_public(gpg, *gpg->k); + if (r) { + return ERR_FAIL; + } + + return ERR_OK; } -int gpg_sign(gcry_sexp_t *out, gcry_sexp_t *key, const char *v) { +static int gpg_sign_sexp(gcry_sexp_t *out, gcry_sexp_t *key, const char *v) { gcry_error_t e; gcry_sexp_t data; size_t err_offset; @@ -316,6 +468,7 @@ int gpg_sign(gcry_sexp_t *out, gcry_sexp_t *key, const char *v) { return 0; } + int gpg_verify(gcry_sexp_t *sig, gcry_sexp_t *key, const char *v) { gcry_error_t e; gcry_sexp_t data; @@ -354,9 +507,18 @@ int gpg_store_digest(struct gpg_store *gpg, char *out, const char *in) { /// \todo handle path length limit void gpg_store_init(struct gpg_store *gpg, const char *path) { + char *p; + size_t c; + memset(gpg, 0, sizeof(struct gpg_store)); gpg->passphrase_digest_len = gcry_md_get_algo_dlen(GCRY_MD_SHA256); - strcpy(gpg->path, path); + strcpy(gpg->path, path); + c = strlen(gpg->path); + p = gpg->path+c; + if (*p != '/') { + *p = '/'; + *(p+1) = 0; + } } int gpg_store_check(struct gpg_store *gpg, const char *passphrase) { @@ -364,7 +526,6 @@ int gpg_store_check(struct gpg_store *gpg, const char *passphrase) { const char *v; char d[1024]; gcry_sexp_t k; - gcry_sexp_t o; char *p; unsigned char fingerprint[20] = { 0x00 }; size_t fingerprint_len = 41; @@ -386,9 +547,9 @@ int gpg_store_check(struct gpg_store *gpg, const char *passphrase) { gpgVersion = v; sprintf(d, "Using gpg version: %s", gpgVersion); debug_log(DEBUG_INFO, d); - //r = key_from_path(&k, p.c_str(), passphrase_hash); - //r = key_from_path(&k, p, passphrase_hash); - r = key_from_path(&k, gpg->path, passphrase_hash); + //r = key_from_file(&k, p.c_str(), passphrase_hash); + //r = key_from_file(&k, p, passphrase_hash); + r = key_from_file(&k, gpg->path, passphrase_hash); if (r == ERR_KEYFAIL) { char pp[2048]; //sprintf(pp, "could not decrypt key in %s/key.bin", p.c_str()); @@ -400,7 +561,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_file(&k, pp, passphrase_hash); + r = key_create_file(gpg, &k, passphrase_hash); if (r != ERR_OK) { return r; } @@ -420,6 +581,132 @@ int gpg_store_check(struct gpg_store *gpg, const char *passphrase) { sprintf(pp, "found key %s in %s", (unsigned char*)gpg->fingerprint, p); debug_log(DEBUG_INFO, pp); } - r = gpg_sign(&o, &k, sign_test); - return r; + //r = gpg_sign(&o, &k, sign_test); + //return r; + return ERR_OK; +} + +int gpg_store_sign(struct gpg_store *gpg, char *data, size_t data_len, const char *passphrase) { + return gpg_store_sign_with(gpg, data, data_len, passphrase, NULL); +} + +int gpg_store_sign_with(struct gpg_store *gpg, char *data, size_t data_len, const char *passphrase, const char *fingerprint) { + int r; + size_t c; + gcry_sexp_t key; + gcry_sexp_t pnt; + gcry_sexp_t msg; + gcry_sexp_t sig; + gcry_error_t e; + char *p; + + r = calculate_digest_algo(data, data_len, gpg->last_data, GCRY_MD_SHA512); + if (r) { + return 1; + } + + gpg->k = &key; + if (fingerprint == NULL) { + r = gpg_key_load(gpg, passphrase, KEE_GPG_FIND_MAIN, NULL); + } else { + r = gpg_key_load(gpg, passphrase, KEE_GPG_FIND_FINGERPRINT, fingerprint); + } + if (r) { + return 1; + } + + c = 0; + e = gcry_sexp_build(&msg, &c, "(data(flags eddsa)(hash-algo sha512)(value %b))", 64, gpg->last_data); + if (e != GPG_ERR_NO_ERROR) { + return 1; + } + + e = gcry_pk_sign(&sig, msg, *gpg->k); + if (e != GPG_ERR_NO_ERROR) { + return 1; + } + + // retrieve r and write it + pnt = NULL; + pnt = gcry_sexp_find_token(sig, "r", 1); + if (pnt == NULL) { + return 1; + } + c = POINT_LENGTH; + p = gcry_sexp_nth_data(pnt, 1, &c); + if (p == NULL) { + return 1; + } + memcpy(gpg->last_signature, p, c); + + // retrieve s and write it + pnt = NULL; + pnt = gcry_sexp_find_token(sig, "s", 1); + if (pnt == NULL) { + return 1; + } + c = POINT_LENGTH; + p = gcry_sexp_nth_data(pnt, 1, &c); + if (p == NULL) { + return 1; + } + memcpy(gpg->last_signature + POINT_LENGTH, p, c); + + gcry_sexp_release(*gpg->k); + gpg->k = NULL; + + return 0; +} + +int gpg_store_verify(const char *sig_bytes, const char *digest, const char *pubkey_bytes) { + gcry_mpi_t sig_r; + gcry_mpi_t sig_s; + size_t c; + gcry_error_t err; + gcry_sexp_t sig; + gcry_sexp_t msg; + gcry_sexp_t pubkey; + + c = 0; + err = gcry_sexp_build(&pubkey, &c, "(key-data(public-key(ecc(curve Ed25519)(q %b))))", PUBKEY_LENGTH, pubkey_bytes); + if (err != GPG_ERR_NO_ERROR) { + return 1; + } + + c = 0; + err = gcry_mpi_scan(&sig_r, GCRYMPI_FMT_STD, sig_bytes, POINT_LENGTH, &c); + if (err != GPG_ERR_NO_ERROR) { + return 1; + } + if (c != 32) { + return 1; + } + + c = 0; + err = gcry_mpi_scan(&sig_s, GCRYMPI_FMT_STD, sig_bytes + POINT_LENGTH, POINT_LENGTH, &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)))", sig_r, sig_s); + if (err != GPG_ERR_NO_ERROR) { + return 1; + } + + c = 0; + err = gcry_sexp_build(&msg, &c, "(data(flags eddsa)(hash-algo sha512)(value %b))", DIGEST_LENGTH, digest); + if (err != GPG_ERR_NO_ERROR) { + return 1; + } + + err = gcry_pk_verify(sig, msg, pubkey); + if (err != GPG_ERR_NO_ERROR) { + return 1; + } + + return 0; } diff --git a/src/gpg.h b/src/gpg.h @@ -9,16 +9,28 @@ #define CHACHA20_KEY_LENGTH_BYTES 32 #define CHACHA20_NONCE_LENGTH_BYTES 12 #define PUBKEY_LENGTH 32 +#define FINGERPRINT_LENGTH 20 +#define SIGNATURE_LENGTH 64 +#define DIGEST_LENGTH 64 +#define POINT_LENGTH 32 #ifndef ENCRYPT_BLOCKSIZE #define ENCRYPT_BLOCKSIZE 4096 #endif +enum gpg_find_mode_e { + KEE_GPG_FIND_MAIN, + KEE_GPG_FIND_FINGERPRINT, +}; struct gpg_store { + gcry_sexp_t *k; size_t passphrase_digest_len; - char fingerprint[40]; + char fingerprint[FINGERPRINT_LENGTH]; + char public_key[PUBKEY_LENGTH]; char path[1024]; + char last_signature[SIGNATURE_LENGTH]; + char last_data[DIGEST_LENGTH]; }; /** @@ -89,7 +101,12 @@ 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); -int gpg_sign(gcry_sexp_t *out, gcry_sexp_t *key, const char *v); +//int gpg_key_create(gcry_sexp_t *key); +int gpg_key_create(struct gpg_store *gpg, const char *passphrase); +//int gpg_sign(gcry_sexp_t *out, gcry_sexp_t *key, const char *v); +int gpg_key_load(struct gpg_store *gpg, const char *passphrase, enum gpg_find_mode_e mode, void *criteria); +int gpg_store_sign(struct gpg_store *gpg, char *data, size_t data_len, const char *passphrase); +int gpg_store_sign_with(struct gpg_store *gpg, char *data, size_t data_len, const char *passphrase, const char *fingerprint); +int gpg_store_verify(const char *sig_bytes, const char *digest, const char *pubkey_bytes); #endif diff --git a/src/gtk/kee-entry-item.c b/src/gtk/kee-entry-item.c @@ -31,6 +31,7 @@ struct _KeeEntryItem { G_DEFINE_TYPE(KeeEntryItem, kee_entry_item, GTK_TYPE_BOX); + static void kee_entry_item_dispose(GObject *o) { } @@ -74,3 +75,31 @@ void kee_entry_item_apply_list_item_widget(KeeEntryItem *o) { gtk_box_append(GTK_BOX(o), widget); return; } + + +void kee_entry_item_apply_edit_widget(GtkBox *box, struct kee_entry_item_form_t *form, int first) { + GtkWidget *widget; + + if (first) { + widget = gtk_label_new("Initial credit"); + } else { + widget = gtk_label_new("Credit change"); + } + gtk_box_append(box, widget); + widget = gtk_entry_new(); + form->alice_credit_delta = GTK_ENTRY(widget); + gtk_entry_set_input_purpose(form->alice_credit_delta, GTK_INPUT_PURPOSE_NUMBER); + gtk_box_append(box, widget); + + if (first) { + widget = gtk_label_new("Initial collateral"); + } else { + widget = gtk_label_new("Collateral change"); + } + gtk_box_append(box, widget); + widget = gtk_entry_new(); + form->alice_collateral_delta = GTK_ENTRY(widget); + gtk_entry_set_input_purpose(form->alice_collateral_delta, GTK_INPUT_PURPOSE_NAME); + gtk_box_append(box, widget); + +} diff --git a/src/gtk/kee-entry-item.h b/src/gtk/kee-entry-item.h @@ -10,6 +10,14 @@ G_BEGIN_DECLS + +struct kee_entry_item_form_t { + GtkEntry *alice_credit_delta; + GtkEntry *alice_collateral_delta; + GtkEntry *bob_credit_delta; + GtkEntry *bob_collateral_delta; +}; + enum KEE_ENTRY_ITEM_PROPS { KEE_P_ENTRY_ITEM_ALICE_CREDIT_DELTA = 1, KEE_P_ENTRY_ITEM_BOB_CREDIT_DELTA, @@ -27,6 +35,7 @@ void kee_entry_item_handle_bind(GtkListItemFactory *o, GtkListItem *item); void kee_entry_item_set_resolver(KeeEntryItem *o, struct Cadiz *resolver); int kee_entry_item_deserialize(KeeEntryItem *o, const char *data, size_t data_len); void kee_entry_item_apply_list_item_widget(KeeEntryItem *o); +void kee_entry_item_apply_edit_widget(GtkBox *box, struct kee_entry_item_form_t *form, int first); G_END_DECLS diff --git a/src/gtk/kee-entry.c b/src/gtk/kee-entry.c @@ -45,6 +45,7 @@ struct kee_entry_form_t { GtkEntry *uoa; GtkEntry *uoa_decimals; GtkEntry *passphrase; + struct kee_entry_item_form_t item_form; }; /// \todo factor out separate struct for listitem @@ -69,23 +70,40 @@ struct _KeeEntry { G_DEFINE_TYPE(KeeEntry, kee_entry, GTK_TYPE_BOX); static void kee_entry_handle_add(GtkButton *butt, KeeEntry *o) { + struct kee_ledger_item_t *item; GtkEntryBuffer *buf; char *b; + size_t c; buf = gtk_entry_get_buffer(o->form->uoa); b = (char*)gtk_entry_buffer_get_text(buf); strcpy(o->ledger.uoa, b); - buf = gtk_entry_get_buffer(o->form->uoa); + buf = gtk_entry_get_buffer(o->form->uoa_decimals); b = (char*)gtk_entry_buffer_get_text(buf); o->ledger.uoa_decimals = (char)atoi(b); - buf = gtk_entry_get_buffer(o->form->uoa); - if (gtk_entry_buffer_get_length(buf) != PUBKEY_LENGTH * 2) { + item = kee_ledger_add_item(&o->ledger); + item->initiator = ALICE; + + buf = gtk_entry_get_buffer(o->form->item_form.alice_credit_delta); + b = (char*)gtk_entry_buffer_get_text(buf); + item->alice_credit_delta = atoi(b); + + buf = gtk_entry_get_buffer(o->form->item_form.alice_collateral_delta); + b = (char*)gtk_entry_buffer_get_text(buf); + item->alice_collateral_delta = atoi(b); + + buf = gtk_entry_get_buffer(o->form->bob_pubkey); + b = (char*)gtk_entry_buffer_get_text(buf); + c = hex2bin(b, (unsigned char*)o->ledger.pubkey_bob); + if (c == 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "invalid counterparty public key data"); + return; + } else if (c != PUBKEY_LENGTH) { g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "wrong size for counterparty public key"); return; } - memcpy(o->ledger.pubkey_bob, o->form->bob_pubkey, PUBKEY_LENGTH); o->state |= ENTRYSTATE_LOAD; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "adding ledger entry"); @@ -219,6 +237,8 @@ static void kee_entry_setup_edit_widget(KeeEntry *o) { gtk_entry_set_max_length(o->form->uoa_decimals, 2); gtk_box_append(GTK_BOX(o->edit), widget); + kee_entry_item_apply_edit_widget(GTK_BOX(o->edit), &o->form->item_form, 1); + /// \todo DRY - kee-key.c widget = gtk_label_new("private key passphrase"); gtk_box_append(GTK_BOX(o->edit), widget); @@ -290,6 +310,7 @@ static void kee_entry_init_list_widget(KeeEntry *o) { sel = gtk_single_selection_new(G_LIST_MODEL(model)); view = gtk_list_view_new(GTK_SELECTION_MODEL(sel), GTK_LIST_ITEM_FACTORY(factory)); gtk_box_append(GTK_BOX(o->display), GTK_WIDGET(view)); + } int kee_entry_deserialize(KeeEntry *o, const char *data, size_t data_len) { diff --git a/src/ledger.c b/src/ledger.c @@ -139,35 +139,40 @@ static int verify_item(struct kee_ledger_t *ledger, asn1_node item, const char * 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) { + r = gpg_store_verify(sig_data, p, pubkey_last_data); + if (r) { 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_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(&msg, &c, "(data(flags eddsa)(hash-algo sha512)(value %b))", 64, p); - if (err != GPG_ERR_NO_ERROR) { - 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; @@ -176,22 +181,22 @@ static int verify_item(struct kee_ledger_t *ledger, asn1_node item, const char * // 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; - } +// 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; +// } - err = gcry_pk_verify(sig, msg, pubkey); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } +// err = gcry_pk_verify(sig, msg, pubkey); +// if (err != GPG_ERR_NO_ERROR) { +// return 1; +// } return 0; } @@ -216,13 +221,22 @@ void kee_ledger_reset_cache(struct kee_ledger_t *ledger) { } } +struct kee_ledger_item_t *kee_ledger_add_item(struct kee_ledger_t *ledger) { + struct kee_ledger_item_t *prev; + + prev = ledger->last_item; + ledger->last_item = calloc(sizeof(struct kee_ledger_item_t), 1); + ledger->last_item->prev_item = prev; + + return ledger->last_item; +} + 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; @@ -231,10 +245,7 @@ struct kee_ledger_item_t *kee_ledger_parse_item(struct kee_ledger_t *ledger, con char tmp[64]; 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; + cur = kee_ledger_add_item(ledger); memset(&root, 0, sizeof(root)); memset(&item, 0, sizeof(item)); @@ -608,3 +619,70 @@ int kee_ledger_item_serialize(struct kee_ledger_item_t *item, char *out, size_t return 0; } + +static int kee_ledger_digest(struct kee_ledger_t *ledger, char *out, size_t out_len) { + int r; + char out_data[1024]; + + r = kee_ledger_serialize(ledger, out_data, &out_len); + if (r) { + return r; + } + + r = calculate_digest_algo(out_data, out_len, out, GCRY_MD_SHA512); + if (r) { + return ERR_FAIL; + } + + return ERR_OK; +} + +//int kee_ledger_sign(struct kee_ledger_t *ledger, char *out, size_t *out_len) { +// char *p; +// kee_ledger_item_t *item; +// char *signature_request; +// size_t c +// size_t l; +// enum kee_item_serialize_mode_e mode; +// +// p = out; +// c = *out_len; +// l = *out_len; +// *out_len = 0; +// +// item = ledger->last_item; +// +// if (item->initiator == BOB) { +// mode = KEE_LEDGER_ITEM_SERIALIZE_RESPONSE; +// } else { +// signature_request = item->alice_signature; +// } +// +// if (memcmp(signature_request, zero_content, 64)) { +// return ERR_ALREADY_SIGNED; +// } +// +// r = kee_ledger_digest(ledger, p, &c); +// if (r) { +// return ERR_FAIL; +// } +// p = out + c; +// l -= c; +// c = l; +// +// r = kee_ledger_serialize(ledger, p, &c); +// if (r) { +// return ERR_FAIL; +// } +// p = out + c; +// l -= c; +// c = l; +// +// r = kee_ledger_item_serialize(ledger, p, &c, KEE_LEDGER_ITEM_SERIALIZE_REQUEST); +// if (r) { +// return ERR_FAIL; +// } +// +// +// return ERR_OK; +//} diff --git a/src/ledger.h b/src/ledger.h @@ -51,6 +51,7 @@ 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); +struct kee_ledger_item_t *kee_ledger_add_item(struct kee_ledger_t *ledger); 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); diff --git a/src/tests/ledger.c b/src/tests/ledger.c @@ -3,11 +3,11 @@ #include "ledger.h" #include "hex.h" -const char *test_ledger_data = "30818e0c035553440201020420c042f26197b312fef5def17e8c7f978c3219f74981f8430cd3bd57116014d33904201613476c3986b1317fbd831a07665210a1c271f1d88a04e8aa804bb032b44f6b04403aba8490187f543270b48e770bb272021f8fdb22f584080fc9d4d02553f4504624122d99ffc9254cdb8a2ef3826e47f60e1e7b5ec28b635fbba1141e3f486aad"; +const char *test_ledger_data = "30818e0c035553440201020420c67ee54f93d63d00f4b8c9a7e1c11b39657b55c525704bb32e15ec85bc140d140420adcaf6474132ac36e97d3dbee693d3b186cd8399d402dc505073069c46b5bd780440878102c19c032fd0d06f6b054a01e969b823ccfe7d5ba37a37beef3e64feb5f9b38e1a0f7413b781a4626b884f89bb3052f662692c53578453dc7c7d911d8609"; -const char *test_item_data_a = "3082011d044000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020817c7aecdfb6a9cbd020212d8020219600440c333b18dfa822c3ce875de07d54d932ed631aaa7f996153a120bda81e2f0717f4f5e67547019bcd8af0ba6ecba95313bbdc5e43385670d67c24ef76879514ad6044010b17b8c2bc18d7c040f8a4927d20714b78f48f6c3be23867f0ba4aac96b61081f86157942620852c2a47d12a3fb066604018d4228908cf5b0c29568fd67bf0c0101ff0440d6ea4b6501aa6c87cc67bc3297a999a53d8d756873656a0ad68ce9a3b5f9be3d974acf17f4d54d27f9ecf8e854fed32b6c672fa1378142f8898ebd9d68a8540f00"; +const char *test_item_data_a = "3082011d044000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020817c94f8dec0e485d0202150e0202107a04404919455218d5bfb6ae9f0de9af37c23c140f976d4a0cb8652fc6897e6f043bc454021128b8daa18eb36c28687cfc33c3e1aa9b4e37059822ae14a0cefd1087a704409bd071c737b9342ab87de3f3e43ccba508366d09b28a6e331f1255d5668b211694fe011c78b6bba376590a5dae47e2ff880facad68e9ab4fca15309c00a6bd0a0101ff04402a3eac6ff818857883fb26d052fb17f2384f9fdb60f5bbae7d849bf621dfc65e68c2b82359b6c54b041732f11919ab0c1ae1a68504870c872f30cc74f9b9ae0400"; -const char *test_item_data_b = "3082011d0440534f94460b11203ec7272d5e5ec67c7d5c2669d0db4b732ce987f7f9a94e6a00d34622d466a1839feedc4e0b690af900baa631ce4cafd67305f5b9c0f8f8faaa020817c7aecdfb94675e02021d4a02020cd0044035c13f852ed6a20385959560c61e780dafebffa44d1e96f9293db1e75bf32b7cee53f3e96beeaeeebdf49e4467727f101b35a3e4a536c8545ec84b2880ce9b0d04403107700183297208a870aa15dcf1ca5da4b59adbc9388eceeb1a859259cd1b0ad7510c67b92a51fc9fc60b1cf7657976b99839ef7bd26134a4d5fed4cf2b820d0101ff044083ba7fd4d08541dee2a3f13189194454d429440fe7799d5e8536d2e18e6a5006487746b68f16fb2534a23372814bce45455e6269d187741217e505ff6f73d70a01"; +const char *test_item_data_b = "3082011d0440c2b795d9d3183bcc9d6ae1ae2960c302d7364a04996013dd9f31be628c46d2ee87b0cba51db67cd851a64dba04cc3e191dd48e7d7f3e063b0c850fd7b9b82218020817c94f8dec3e67aa02020ce20202049504401f78629f3015afa72f443005fc6711f7a7e2e20072eac86c98874c1dbe42095de3408d5711fb8fca56428461139992e8ff0452dc2092d2ba6ddb9658607f90ac0440d5d6cd6d905d0eb104ff3ab825cfc1be27f69a5377a3c84c33b3c5a0e6902e2af74d9024db58e1b90375be316e687a928edb881f8b6b3795682c20e533f9ed040101ff04409e8ffbbd5684b75aed7bf42a044914ea5813b1fccd9645462664317fa92dd9766c9ede39ea381e9648ef88bad220d0808660be63c94bf9954cf00daddad1150e01"; int main() { diff --git a/src/tests/sign.c b/src/tests/sign.c @@ -1,4 +1,6 @@ #include <gcrypt.h> +#include <stdlib.h> +#include <fcntl.h> #include "ledger.h" #include "gpg.h" @@ -7,24 +9,18 @@ const char *content_test = "Subject: foo\n\nsome content\n"; const char *content_test_item = "Subject: bar\n\nsome other content\n"; -int main() { +/// \todo split up function +int test_sign() { int r; - size_t c; gcry_sexp_t alice; - gcry_sexp_t alice_pub; gcry_sexp_t bob; - gcry_sexp_t bob_pub; - gcry_sexp_t tmp; - gcry_sexp_t sig; - gcry_sexp_t msg; - gcry_mpi_t sr; - gcry_mpi_t ss; - gcry_error_t err; + char alice_fingerprint[20]; char *p; char *out; size_t out_len; char *out_item; size_t out_item_len; + struct gpg_store gpg; struct kee_ledger_t ledger; struct kee_ledger_item_t item; struct kee_ledger_item_t *item_parsed; @@ -32,6 +28,7 @@ int main() { struct kee_content_t content_item; char item_sum[64]; const char *version; + char path[1024]; version = gcry_check_version(NULL); if (version == 0x0) { @@ -40,46 +37,30 @@ int main() { gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); - r = gpg_key_create(&alice); - if (r) { - return 1; - } - r = gpg_key_create(&bob); - if (r) { + strcpy(path, "/tmp/keetest_key_XXXXXX"); + p = mkdtemp(path); + if (p == NULL) { 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) { + gpg_store_init(&gpg, p); + gpg.k = &alice; + r = gpg_key_create(&gpg, "1234"); // alice + if (r) { return 1; } - memcpy(ledger.pubkey_alice, out, 32); + memcpy(ledger.pubkey_alice, gpg.public_key, PUBKEY_LENGTH); + memcpy(alice_fingerprint, gpg.fingerprint, FINGERPRINT_LENGTH); - 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) { + gpg_store_init(&gpg, p); + gpg.k = &bob; + r = gpg_key_create(&gpg, "1234"); // bob + if (r) { return 1; } - memcpy(ledger.pubkey_bob, out, 32); + memcpy(ledger.pubkey_bob, gpg.public_key, PUBKEY_LENGTH); strcpy(ledger.uoa, "USD"); ledger.uoa_decimals = 2; @@ -92,7 +73,6 @@ int main() { if (r) { return 1; } - r = calculate_digest_algo(content_test, strlen(content_test), content.key, GCRY_MD_SHA512); if (r) { return 1; @@ -105,7 +85,7 @@ int main() { return 1; } - r = calculate_digest_algo(out, out_len, ledger.digest, GCRY_MD_SHA512); + r = calculate_digest_algo(out, out_len, ledger.digest, GCRY_MD_SHA512); if (r) { return 1; } @@ -122,7 +102,7 @@ int main() { item.initiator = BOB; item.response = 1; - r = calculate_digest_algo(content_test, strlen(content_test_item), content_item.key, GCRY_MD_SHA512); + r = calculate_digest_algo(content_test_item, strlen(content_test_item), content_item.key, GCRY_MD_SHA512); if (r) { return 1; } @@ -130,6 +110,10 @@ int main() { if (r) { return 1; } + r = calculate_digest_algo(content_test_item, strlen(content_test_item), content_item.key, GCRY_MD_SHA512); + if (r) { + return 1; + } out_item_len = 4096; out_item = malloc(out_item_len); @@ -141,58 +125,15 @@ int main() { if (r) { return 1; } - r = gpg_sign(&sig, &bob, item_sum); + r = gpg_store_sign(&gpg, out_item, out_item_len, "1234"); if (r) { return 1; } - c = 0; - bob_pub = gcry_sexp_find_token(bob, "public-key", 10); - if (bob_pub == NULL) { - return 1; - } - err = gcry_sexp_build(&bob_pub, &c, "(key-data%S)", bob_pub); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } - r = gpg_verify(&sig, &bob, item_sum); + r = gpg_store_verify(gpg.last_signature, item_sum, ledger.pubkey_bob); if (r) { return 1; } - - tmp = gcry_sexp_find_token(sig, "r", 1); - if (tmp == NULL) { - return 1; - } - sr = gcry_sexp_nth_mpi(tmp, 1, GCRYMPI_FMT_STD); - if (sr == NULL) { - return 1; - } - tmp = gcry_sexp_find_token(sig, "s", 1); - if (tmp == NULL) { - return 1; - } - ss = gcry_sexp_nth_mpi(tmp, 1, GCRYMPI_FMT_STD); - if (ss == NULL) { - return 1; - } - c = 0; - err = gcry_mpi_print(GCRYMPI_FMT_STD, item_sum, 32, &c, sr); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } - if (c != 32) { - return 1; - } - memcpy(item.bob_signature, item_sum, 32); - c = 0; - err = gcry_mpi_print(GCRYMPI_FMT_STD, item_sum, 32, &c, ss); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } - if (c != 32) { - return 1; - } - memcpy(item.bob_signature+32, item_sum, 32); + memcpy(item.bob_signature, gpg.last_signature, SIGNATURE_LENGTH); out_item_len = 4096; r = kee_ledger_item_serialize(&item, out_item, &out_item_len, KEE_LEDGER_ITEM_SERIALIZE_RESPONSE); @@ -203,59 +144,16 @@ int main() { if (r) { return 1; } - r = gpg_sign(&sig, &alice, item_sum); + gpg.k = &alice; + r = gpg_store_sign_with(&gpg, out_item, out_item_len, "1234", alice_fingerprint); if (r) { return 1; } - c = 0; - alice_pub = gcry_sexp_find_token(alice, "public-key", 10); - if (alice_pub == NULL) { - return 1; - } - err = gcry_sexp_build(&alice_pub, &c, "(key-data%S)", alice_pub); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } - r = gpg_verify(&sig, &alice, item_sum); + r = gpg_store_verify(gpg.last_signature, item_sum, ledger.pubkey_alice); if (r) { return 1; } - tmp = NULL; - tmp = gcry_sexp_find_token(sig, "r", 1); - if (tmp == NULL) { - return 1; - } - sr = gcry_sexp_nth_mpi(tmp, 1, GCRYMPI_FMT_STD); - if (sr == NULL) { - return 1; - } - tmp = NULL; - tmp = gcry_sexp_find_token(sig, "s", 1); - if (tmp == NULL) { - return 1; - } - ss = gcry_sexp_nth_mpi(tmp, 1, GCRYMPI_FMT_STD); - if (ss == NULL) { - return 1; - } - c = 0; - err = gcry_mpi_print(GCRYMPI_FMT_STD, item_sum, 32, &c, sr); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } - if (c != 32) { - return 1; - } - memcpy(item.alice_signature, item_sum, 32); - c = 0; - err = gcry_mpi_print(GCRYMPI_FMT_STD, item_sum, 32, &c, ss); - if (err != GPG_ERR_NO_ERROR) { - return 1; - } - if (c != 32) { - return 1; - } - memcpy(item.alice_signature+32, item_sum, 32); + memcpy(item.alice_signature, gpg.last_signature, SIGNATURE_LENGTH); out_item_len = 4096; r = kee_ledger_item_serialize(&item, out_item, &out_item_len, KEE_LEDGER_ITEM_SERIALIZE_FINAL); @@ -268,7 +166,6 @@ int main() { if (item_parsed == NULL) { return 1; } - tmp = NULL; free(out_item); free(out); @@ -278,3 +175,46 @@ int main() { return 0; } + +int test_create() { + char *p; + int r; + struct gpg_store gpg; + char path[1024]; + gcry_sexp_t key; + + strcpy(path, "/tmp/keetest_key_XXXXXX"); + p = mkdtemp(path); + if (p == NULL) { + return 1; + } + gpg_store_init(&gpg, p) + ; + r = gpg_key_create(&gpg, "1234"); + if (r) { + return 1; + } + + memset(&gpg, 0, sizeof(struct gpg_store)); + gpg_store_init(&gpg, p); + gpg.k = &key; + r = gpg_key_load(&gpg, "1234", KEE_GPG_FIND_MAIN, NULL); + if (r) { + return 1; + } + + return 0; +} + +int main() { + int r; + r = test_create(); + if (r) { + return 1; + } + r = test_sign(); + if (r) { + return 1; + } + return 0; +} diff --git a/testdata_asn1.py b/testdata_asn1.py @@ -457,8 +457,8 @@ if __name__ == '__main__': keys = ['alice'] alice_key = os.path.join(crypto_dir, 'alice.key.bin') - os.unlink('key.bin') - os.symlink(alice_key, 'key.bin') + os.unlink('kee.key') + os.symlink(alice_key, 'kee.key') count_ledgers = os.environ.get('COUNT', '1')