kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 2c7434a4beb4f2c026bd1713c26008e4725174cd
parent 29589741aac6f8b540851739e81997479a19ad73
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 13:22:49 -0700

selfdist: kit pkg sign (detached minisig, stock-minisign verifiable) + trusted_keys single-root reconcile (KIT_HOME/config + legacy fallback)

Diffstat:
Mdriver/cmd/pkg.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Minclude/kit/package.h | 12++++++++++++
Msrc/api/package.c | 25+++++++++++++++++++++++++
3 files changed, 134 insertions(+), 8 deletions(-)

diff --git a/driver/cmd/pkg.c b/driver/cmd/pkg.c @@ -32,6 +32,7 @@ void driver_help_pkg(void) { "kpkg|tar.gz]\n" " [--external DIR] FILE -C DIR\n" " kit pkg inspect [--manifest | --encoding] FILE\n" + " kit pkg sign -s SECKEY [-o OUT] [--comment C] FILE\n" " kit pkg trust {path | list | add PUBKEY [label] | remove KEYID}\n"); } @@ -67,15 +68,26 @@ static void pkg_parent_dir(const char* path, char* buf, size_t cap) { buf[n] = '\0'; } +/* Preferred trusted-keys path under the single-root layout: $KIT_TRUSTED_KEYS, + * else $KIT_HOME/config/trusted_keys (with $KIT_HOME defaulting per + * driver_kit_home). Used for both reads and the tofu/trust writes. */ static int pkg_trust_path(char* buf, size_t cap) { const char* env = driver_getenv("KIT_TRUSTED_KEYS"); - const char* home; - if (env) { + char home[PKG_PATH_BUF]; + if (env && *env) { snprintf(buf, cap, "%s", env); return 0; } - home = driver_getenv("HOME"); - if (!home) return 1; + if (driver_kit_home(home, sizeof home) != 0) return 1; + snprintf(buf, cap, "%s/config/trusted_keys", home); + return 0; +} + +/* Legacy pre-single-root location, still honored for reads so existing setups + * keep working: $HOME/.config/kit/trusted_keys. Returns 0 on success. */ +static int pkg_trust_legacy_path(char* buf, size_t cap) { + const char* home = driver_getenv("HOME"); + if (!home || !*home) return 1; snprintf(buf, cap, "%s/.config/kit/trusted_keys", home); return 0; } @@ -392,10 +404,17 @@ static int pkg_verify_or_unpack(DriverEnv* env, const KitContext* ctx, int argc, } have_tpath = (pkg_trust_path(tpath, sizeof tpath) == 0); if (!have_tpath) tpath[0] = '\0'; - if (have_tpath && !pubkey && pkg_read(ctx, tpath, &trustfd)) { - trust_loaded = 1; - opts.trusted_keys = trustfd.data; - opts.trusted_keys_len = trustfd.size; + if (!pubkey) { + char legacy[PKG_PATH_BUF]; + if (have_tpath && pkg_read(ctx, tpath, &trustfd)) + trust_loaded = 1; + else if (pkg_trust_legacy_path(legacy, sizeof legacy) == 0 && + pkg_read(ctx, legacy, &trustfd)) + trust_loaded = 1; /* fall back to the pre-single-root location */ + if (trust_loaded) { + opts.trusted_keys = trustfd.data; + opts.trusted_keys_len = trustfd.size; + } } host = driver_cas_host(env); @@ -597,6 +616,74 @@ static int pkg_trust(DriverEnv* env, const KitContext* ctx, int argc, return 2; } +/* ---------------------------------------------------------------------- */ +/* sign: detached minisign signature over a file */ +/* ---------------------------------------------------------------------- */ + +static int pkg_sign(DriverEnv* env, const KitContext* ctx, int argc, + char** argv) { + const char *file = NULL, *seckey = NULL, *out_path = NULL, *comment = NULL; + KitFileData msgfd, skfd; + KitWriter* w = NULL; + char out_buf[PKG_PATH_BUF]; + int msg_loaded = 0, sk_loaded = 0, i, rc = 1; + KitStatus st; + (void)env; + for (i = 0; i < argc; ++i) { + if (driver_streq(argv[i], "-s") && i + 1 < argc) + seckey = argv[++i]; + else if (driver_streq(argv[i], "-o") && i + 1 < argc) + out_path = argv[++i]; + else if (driver_streq(argv[i], "--comment") && i + 1 < argc) + comment = argv[++i]; + else if (argv[i][0] != '-') + file = argv[i]; + else { + driver_errf(PKG_TOOL, "sign: unknown option: %s", argv[i]); + return 2; + } + } + if (!file || !seckey) { + driver_errf(PKG_TOOL, "sign: FILE and -s SECKEY are required"); + return 2; + } + if (!out_path) { + snprintf(out_buf, sizeof out_buf, "%s.minisig", file); + out_path = out_buf; + } + if (!pkg_read(ctx, file, &msgfd)) { + driver_errf(PKG_TOOL, "sign: cannot read file: %s", file); + return 1; + } + msg_loaded = 1; + if (!pkg_read(ctx, seckey, &skfd)) { + driver_errf(PKG_TOOL, "sign: cannot read secret key: %s", seckey); + goto done; + } + sk_loaded = 1; + if (ctx->file_io->open_writer(ctx->file_io->user, out_path, &w) != KIT_OK) { + driver_errf(PKG_TOOL, "sign: cannot open output: %s", out_path); + goto done; + } + st = kit_pkg_sign_detached(ctx, msgfd.data, msgfd.size, skfd.data, skfd.size, + comment, w); + if (st != KIT_OK) driver_writer_abort(w); + kit_writer_close(w); + if (st == KIT_UNSUPPORTED) + driver_errf(PKG_TOOL, "sign: encrypted secret keys need scrypt"); + else if (st != KIT_OK) + driver_errf(PKG_TOOL, "sign: failed to sign %s", file); + else { + driver_printf("wrote %s\n", out_path); + rc = 0; + } + +done: + if (sk_loaded) pkg_release(ctx, &skfd); + if (msg_loaded) pkg_release(ctx, &msgfd); + return rc; +} + int driver_pkg(int argc, char** argv) { DriverEnv env; KitContext ctx; @@ -619,6 +706,8 @@ int driver_pkg(int argc, char** argv) { rc = pkg_verify_or_unpack(&env, &ctx, argc - 2, argv + 2, 1); else if (driver_streq(sub, "inspect")) rc = pkg_inspect(&env, &ctx, argc - 2, argv + 2); + else if (driver_streq(sub, "sign")) + rc = pkg_sign(&env, &ctx, argc - 2, argv + 2); else if (driver_streq(sub, "trust")) rc = pkg_trust(&env, &ctx, argc - 2, argv + 2); else { diff --git a/include/kit/package.h b/include/kit/package.h @@ -63,6 +63,18 @@ KIT_API KitStatus kit_minisig_parse_seckey( const uint8_t* data, size_t len, uint8_t sk_out[KIT_PKG_SK_LEN], uint8_t keyid_out[KIT_PKG_KEYID_LEN]); +/* Write a detached minisign signature over msg[0..msglen) to *out*, signing + * with the passwordless minisign secret-key file content seckey_bytes. The + * signature is interchangeable with stock minisign (`minisign -Vm FILE`), so it + * backs the bootstrap-verification of a release .tar.gz before any kit exists. + * `comment` (NULL for a default) is the signed trusted comment. Returns + * KIT_UNSUPPORTED for scrypt-encrypted keys, KIT_MALFORMED for a bad key. */ +KIT_API KitStatus kit_pkg_sign_detached(const KitContext* ctx, + const uint8_t* msg, size_t msglen, + const uint8_t* seckey_bytes, + size_t seckey_len, const char* comment, + KitWriter* out); + /* Trusted-keys store helpers (the store is plain text managed by the caller). * lookup finds keyid's public key; format_entry renders one NUL-terminated, * newline-included store line. */ diff --git a/src/api/package.c b/src/api/package.c @@ -1721,6 +1721,31 @@ KitStatus kit_calver_compare(const char* a, const char* b, int* cmp) { return dist_calver_compare(a, b, cmp) == DIST_OK ? KIT_OK : KIT_INVALID; } +KitStatus kit_pkg_sign_detached(const KitContext* ctx, const uint8_t* msg, + size_t msglen, const uint8_t* seckey_bytes, + size_t seckey_len, const char* comment, + KitWriter* out) { + uint8_t sk[DIST_ED25519_SK_LEN], keyid[DIST_KEYID_LEN]; + int rc; + const char* tc = (comment && *comment) ? comment : "kit detached signature"; + if (!ctx || (!msg && msglen) || !seckey_bytes || !out) return KIT_INVALID; + rc = dist_minisig_parse_seckey(seckey_bytes, seckey_len, sk, keyid); + if (rc == DIST_ENCRYPTED) { + kit_ctx_diagf(ctx, "encrypted secret keys need scrypt"); + return KIT_UNSUPPORTED; + } + if (rc != DIST_OK) { + kit_ctx_diagf(ctx, "malformed secret key"); + return KIT_MALFORMED; + } + if (dist_minisig_sign(out, msg, msglen, sk, keyid, "kit signature", tc) != + DIST_OK) { + kit_ctx_diagf(ctx, "could not produce signature"); + return KIT_ERR; + } + return kit_writer_status(out) == KIT_OK ? KIT_OK : KIT_IO; +} + KitStatus kit_pkg_keygen(const KitContext* ctx, KitPkgRandomFn rng, void* rng_user, KitWriter* pub_out, KitWriter* sec_out, uint8_t out_keyid[KIT_PKG_KEYID_LEN]) {