commit 40e878e3eff0be3f3a8c07352de05fb1ecd31e3a
parent 06e37e71447fac13a38ab591f4e374a03d32aa7b
Author: lash <dev@holbrook.no>
Date:   Fri, 28 Mar 2025 16:26:08 +0000
IMplement store for store key create
Diffstat:
3 files changed, 259 insertions(+), 153 deletions(-)
diff --git a/src/crypto/gcrypt.c b/src/crypto/gcrypt.c
@@ -108,148 +108,6 @@ int lq_crypto_init(const char *base) {
 	return ERR_OK;
 }
 
-// DIGEST SECTION
-
-/// Calculate a digest according to the specified algo.
-static int calculate_digest_algo(const char *in, size_t in_len, char *out, enum gcry_md_algos algo) {
-	gcry_error_t e;
-	gcry_md_hd_t h;
-	unsigned char *v;
-	static unsigned int digest_len;
-
-	if (algo == GCRY_MD_NONE) {
-		algo = GCRY_MD_SHA256;
-	}
-	digest_len = gcry_md_get_algo_dlen(algo);
-
-	e = gcry_md_open(&h, algo, GCRY_MD_FLAG_SECURE);
-	if (e) {
-		return ERR_ENCODING;
-	}
-
-	gcry_md_write(h, in, in_len);
-	v = gcry_md_read(h, 0);
-	lq_cpy(out, v, digest_len);
-	gcry_md_close(h);
-	return ERR_OK;
-}
-
-/// Calculate digest using the default hashing algorithm (SHA256)
-/// using the gcrypt library.
-int lq_digest(const char *in, size_t in_len, char *out) {
-	return calculate_digest_algo(in, in_len, out, GCRY_MD_NONE);
-}
-
-
-/// Apply public key to the gpg_store struct.
-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 debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
-	}
-	pubkey = gcry_sexp_find_token(pubkey, "q", 1);
-	if (pubkey == NULL) {
-		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
-	}
-	c = LQ_PUBKEY_LEN;
-	p = (char*)gcry_sexp_nth_data(pubkey, 1, &c);
-	if (p == NULL) {
-		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
-	}
-	lq_cpy(gpg->public_key, p, LQ_PUBKEY_LEN);
-	return ERR_OK;
-}
-
-/// Create a new gcrypt keypair.
-static int key_create(struct gpg_store *gpg) {
-	int r;
-	const char *p;
-	const char *sexp_quick = "(genkey(ecc(flags eddsa)(curve Ed25519)))";
-	gcry_sexp_t in;
-	gcry_error_t e;
-
-	e = gcry_sexp_new(&in, (const void*)sexp_quick, strlen(sexp_quick), 0);
-	if (e) {
-		p = gcry_strerror(e);
-		return debug_logerr(LLOG_ERROR, ERR_KEYFAIL, (char*)p);
-	}
-	e = gcry_pk_genkey(&gpg->k, in);
-	if (e) {
-		p = gcry_strerror(e);
-		return debug_logerr(LLOG_ERROR, ERR_KEYFAIL, (char*)p);
-	}
-	p = (char*)gcry_pk_get_keygrip(gpg->k, (unsigned char*)gpg->fingerprint);
-	if (p == NULL) {
-		p = gcry_strerror(e);
-		return debug_logerr(LLOG_ERROR, ERR_KEYFAIL, (char*)p);
-	}
-
-	r = key_apply_public(gpg, gpg->k);
-	if (r) {
-		return debug_logerr(LLOG_ERROR, ERR_KEYFAIL, NULL);
-	}
-
-	return ERR_OK;
-}
-
-
-/// Create a new keypair, encrypted with given passphrase.
-static LQPrivKey* privatekey_alloc(const char *seed, size_t seed_len, const char *passphrase, size_t passphrase_len) {
-	int r;
-	LQPrivKey *o;
-	struct gpg_store *gpg;
-
-	// allocate private key memory
-	o = lq_alloc(sizeof(LQPrivKey));
-	if (o == NULL) {
-		return NULL;
-	}
-
-	// allocate gpg internal private key memory
-	gpg = lq_alloc(sizeof(struct gpg_store));
-	if (gpg == NULL) {
-		lq_free(o);
-		return NULL;
-	}
-
-	// create the underlying private key.
-	r = key_create(gpg);
-	if (r) {
-		lq_free(gpg);
-		lq_free(o);
-		return NULL;
-	}
-
-	// populate the internal key structure
-	o->impl = (void*)gpg;
-	o->key_typ = GPG_KEY_TYP;
-	o->key_state = LQ_KEY_INIT;
-
-	debug_x(LLOG_INFO, "gpg", "created new private key", 1, MORGEL_TYP_BIN, LQ_FP_LEN, "fingerprint", gpg->fingerprint);
-
-	return o;
-}
-
-/// Implements the interface to create a new private key.
-LQPrivKey* lq_privatekey_new(const char *seed, size_t seed_len, const char *passphrase, size_t passphrase_len) {
-	int r;
-	LQPrivKey *o;
-
-	o = privatekey_alloc(seed, seed_len, passphrase, passphrase_len);
-	if (o == NULL) {
-		return NULL;
-	}
-	r = lq_privatekey_lock(o, passphrase, passphrase_len);
-	if (r) {
-		return NULL;
-	}
-	return o;	
-}
-
 size_t get_padsize(size_t insize, size_t blocksize) {
 	size_t c;
 	size_t l;
@@ -398,6 +256,244 @@ int decrypt(char *outdata, const char *ciphertext, size_t ciphertext_len, const 
 
 	return ERR_OK;
 }
+
+
+// DIGEST SECTION
+
+/// Calculate a digest according to the specified algo.
+static int calculate_digest_algo(const char *in, size_t in_len, char *out, enum gcry_md_algos algo) {
+	gcry_error_t e;
+	gcry_md_hd_t h;
+	unsigned char *v;
+	static unsigned int digest_len;
+
+	if (algo == GCRY_MD_NONE) {
+		algo = GCRY_MD_SHA256;
+	}
+	digest_len = gcry_md_get_algo_dlen(algo);
+
+	e = gcry_md_open(&h, algo, GCRY_MD_FLAG_SECURE);
+	if (e) {
+		return ERR_ENCODING;
+	}
+
+	gcry_md_write(h, in, in_len);
+	v = gcry_md_read(h, 0);
+	lq_cpy(out, v, digest_len);
+	gcry_md_close(h);
+	return ERR_OK;
+}
+
+/// Calculate digest using the default hashing algorithm (SHA256)
+/// using the gcrypt library.
+int lq_digest(const char *in, size_t in_len, char *out) {
+	return calculate_digest_algo(in, in_len, out, GCRY_MD_NONE);
+}
+
+
+/// Apply public key to the gpg_store struct.
+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 debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
+	}
+	pubkey = gcry_sexp_find_token(pubkey, "q", 1);
+	if (pubkey == NULL) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
+	}
+	c = LQ_PUBKEY_LEN;
+	p = (char*)gcry_sexp_nth_data(pubkey, 1, &c);
+	if (p == NULL) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
+	}
+	lq_cpy(gpg->public_key, p, LQ_PUBKEY_LEN);
+	return ERR_OK;
+}
+
+/// Create a new gcrypt keypair.
+static int key_create(struct gpg_store *gpg) {
+	int r;
+	const char *p;
+	const char *sexp_quick = "(genkey(ecc(flags eddsa)(curve Ed25519)))";
+	gcry_sexp_t in;
+	gcry_error_t e;
+
+	e = gcry_sexp_new(&in, (const void*)sexp_quick, strlen(sexp_quick), 0);
+	if (e) {
+		p = gcry_strerror(e);
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, (char*)p);
+	}
+	e = gcry_pk_genkey(&gpg->k, in);
+	if (e) {
+		p = gcry_strerror(e);
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, (char*)p);
+	}
+	p = (char*)gcry_pk_get_keygrip(gpg->k, (unsigned char*)gpg->fingerprint);
+	if (p == NULL) {
+		p = gcry_strerror(e);
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, (char*)p);
+	}
+
+	r = key_apply_public(gpg, gpg->k);
+	if (r) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
+	}
+
+	return ERR_OK;
+}
+
+LQStore *key_store_get() {
+	int r;
+	char *p;
+
+	r = lq_config_get(gpg_cfg_idx_dir, (void**)&p);
+	if (r) {
+		return NULL;
+	}
+	return lq_store_new(p);
+}
+//
+//static char *key_filename(LQStore *store, struct gpg_store *gpg, char *out, size_t *out_len) {
+//	int l;
+//
+//	l = strlen(store->userdata);
+//	if (*out_len < (20 + 1 + l)) {
+//		*out_len = 0;
+//		return NULL;
+//	}
+//
+//	lq_cpy(out, store->userdata, l);
+//	b2h((unsigned char*)gpg->fingerprint, 20, (unsigned char*)out+len);
+//
+//	return out;
+//}
+
+/**
+ * \todo consistent endianness for key length in persistent storage (fwrite)
+ * \todo doc must have enough in path for path + fingerprint hex
+ *
+ */
+//static int key_create_file(struct gpg_store *gpg, gcry_sexp_t *key, const char *passphrase) {
+static int key_create_store(LQStore *store, struct gpg_store *gpg, const char *passphrase) {
+	char *p;
+	int r;
+	int kl;
+	char v[LQ_CRYPTO_BUFLEN];
+	int i;
+	int l;
+	size_t c;
+	size_t m;
+	//FILE *f;
+	char nonce[CHACHA20_NONCE_LENGTH_BYTES];
+	char buf_key[LQ_STORE_KEY_MAX];
+	char buf_val[LQ_STORE_VAL_MAX];
+	char ciphertext[LQ_CRYPTO_BUFLEN];
+
+	//r = key_create(gpg, key);
+	r = key_create(gpg);
+	if (r) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
+	}
+
+	kl = gcry_sexp_sprint(gpg->k, GCRYSEXP_FMT_CANON, NULL, 0);
+	m = (size_t)kl + 1;
+	p = (char*)v + sizeof(int);
+	c = 0;
+	kl = gcry_sexp_sprint(gpg->k, GCRYSEXP_FMT_CANON, p, LQ_CRYPTO_BUFLEN - m);
+	m -= (size_t)(kl + 1);
+	c += kl;
+//	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));
+
+	m = c;
+	c = get_padsize(m, LQ_CRYPTO_BLOCKSIZE);
+	gcry_create_nonce(nonce, CHACHA20_NONCE_LENGTH_BYTES);
+	r = encryptb(ciphertext, c, v, m+sizeof(int), passphrase, nonce);
+	if (r) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, "encrypt private key");
+	}
+
+	lq_cpy(buf_val, nonce, CHACHA20_NONCE_LENGTH_BYTES);
+	lq_cpy(buf_val + CHACHA20_NONCE_LENGTH_BYTES, ciphertext, l);
+	*buf_key = LQ_CONTENT_KEY;
+	b2h((unsigned char*)gpg->fingerprint, 20, (unsigned char*)buf_key+1);
+	store = key_store_get();
+	if (store == NULL) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, "create store");
+	}
+	
+	c = CHACHA20_NONCE_LENGTH_BYTES + 1;
+	r = store->put(LQ_CONTENT_KEY, store, buf_key, &c, buf_val, l);
+	if (r) {
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, "put key in store");
+	}
+	lq_free(store);
+
+	return ERR_OK;
+}
+
+/// Create a new keypair, encrypted with given passphrase.
+static LQPrivKey* privatekey_alloc(const char *seed, size_t seed_len, const char *passphrase, size_t passphrase_len) {
+	int r;
+	LQPrivKey *o;
+	struct gpg_store *gpg;
+
+	// allocate private key memory
+	o = lq_alloc(sizeof(LQPrivKey));
+	if (o == NULL) {
+		return NULL;
+	}
+
+	// allocate gpg internal private key memory
+	gpg = lq_alloc(sizeof(struct gpg_store));
+	if (gpg == NULL) {
+		lq_free(o);
+		return NULL;
+	}
+
+	// create the underlying private key.
+	r = key_create(gpg);
+	if (r) {
+		lq_free(gpg);
+		lq_free(o);
+		return NULL;
+	}
+
+	// populate the internal key structure
+	o->impl = (void*)gpg;
+	o->key_typ = GPG_KEY_TYP;
+	o->key_state = LQ_KEY_INIT;
+
+	debug_x(LLOG_INFO, "gpg", "created new private key", 1, MORGEL_TYP_BIN, LQ_FP_LEN, "fingerprint", gpg->fingerprint);
+
+	return o;
+}
+
+/// Implements the interface to create a new private key.
+LQPrivKey* lq_privatekey_new(const char *seed, size_t seed_len, const char *passphrase, size_t passphrase_len) {
+	int r;
+	LQPrivKey *o;
+
+	o = privatekey_alloc(seed, seed_len, passphrase, passphrase_len);
+	if (o == NULL) {
+		return NULL;
+	}
+	r = lq_privatekey_lock(o, passphrase, passphrase_len);
+	if (r) {
+		return NULL;
+	}
+	return o;	
+}
+
 static int key_from_data(gcry_sexp_t *key, const char *indata, size_t indata_len) {
 	gcry_error_t e;
 
@@ -463,7 +559,7 @@ static int gpg_key_load(LQStore *store, struct gpg_store *gpg, const char *passp
 			strcpy(p, GPG_PK_FILENAME);
 			r = key_from_file(&gpg->k, path, passphrase);
 			if (r) {
-				return debug_logerr(LLOG_WARNING, ERR_KEYFAIL, NULL);
+				return debug_logerr(LLOG_WARNING, ERR_CRYPTO, NULL);
 			}
 			break;
 		case GPG_FIND_FINGERPRINT:
@@ -472,7 +568,7 @@ static int gpg_key_load(LQStore *store, struct gpg_store *gpg, const char *passp
 			b2h((const unsigned char*)criteria, LQ_FP_LEN, (unsigned char*)p);
 			r = key_from_file(&gpg->k, path, passphrase);
 			if (r) {
-				return debug_logerr(LLOG_WARNING, ERR_KEYFAIL, NULL);
+				return debug_logerr(LLOG_WARNING, ERR_CRYPTO, NULL);
 			}
 			break;
 		default:
@@ -481,7 +577,7 @@ static int gpg_key_load(LQStore *store, struct gpg_store *gpg, const char *passp
 
 	p = (char*)gcry_pk_get_keygrip(gpg->k, (unsigned char*)gpg->fingerprint);
 	if (p == NULL) {
-		return debug_logerr(LLOG_ERROR, ERR_KEYFAIL, NULL);
+		return debug_logerr(LLOG_ERROR, ERR_CRYPTO, NULL);
 	}
 
 	r = key_apply_public(gpg, gpg->k);
@@ -493,6 +589,7 @@ static int gpg_key_load(LQStore *store, struct gpg_store *gpg, const char *passp
 	return ERR_OK;
 }
 
+
 /// Implements the interface to load a private key from storage.
 LQPrivKey* lq_privatekey_load(const char *passphrase, size_t passphrase_len) {
 	LQStore *store;
@@ -508,17 +605,12 @@ LQPrivKey* lq_privatekey_load(const char *passphrase, size_t passphrase_len) {
 		return NULL;
 	}
 	
-	r = lq_config_get(gpg_cfg_idx_dir, (void**)&p);
-	if (r) {
-		return NULL;
-	}
-
-	gpg = lq_alloc(sizeof(struct gpg_store));
-	store = lq_store_new(p);
+	store = key_store_get();
 	if (store == NULL) {
 		return NULL;
 	}
 
+	gpg = lq_alloc(sizeof(struct gpg_store));
 	r = gpg_key_load(store, gpg, passphrase_hash, GPG_FIND_MAIN, NULL);
 	if (r) {
 		return NULL;	
@@ -527,6 +619,7 @@ LQPrivKey* lq_privatekey_load(const char *passphrase, size_t passphrase_len) {
 	pk->key_typ = GPG_KEY_TYP;
 	pk->key_state = LQ_KEY_INIT;
 	pk->impl = gpg;
+	lq_free(store);
 
 	return pk;
 }
diff --git a/src/lq/crypto.h b/src/lq/crypto.h
@@ -31,6 +31,10 @@
 #define LQ_CRYPTO_BUFLEN 524288
 #endif
 
+#ifndef LQ_CRYPTO_BLOCKSIZE
+#define LQ_CRYPTO_BLOCKSIZE 4096
+#endif
+
 #ifndef LQ_POINT_LEN
 #define LQ_POINT_LEN 32
 #endif
diff --git a/src/lq/store.h b/src/lq/store.h
@@ -3,13 +3,22 @@
 
 #include <stddef.h>
 
+#ifndef LQ_STORE_KEY_MAX
+#define LQ_STORE_KEY_MAX 64
+#endif
+
+#ifndef LQ_STORE_VAL_MAX
+#define LQ_STORE_VAL_MAX 65536
+#endif
+
 /// Payload type hint to control how and what a store implementation executes persistence.
 /// Not currently in active use.
 enum payload_e {
 	LQ_CONTENT_RAW, ///< Arbitrary data.
 	LQ_CONTENT_MSG, ///< Data is a message type.
 	LQ_CONTENT_CERT, ///< Data is a cert type.
-	LQ_CONTENT_KEY, ///< Data is a public key type.
+	LQ_CONTENT_KEY, ///< Data is a private key type.
+	LQ_CONTENT_KEY_PUBLIC, ///< Data is a public key type.
 };
 
 /**