kit

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

pkg.c (21816B)


      1 #include <kit/cas.h>
      2 #include <kit/core.h>
      3 #include <kit/package.h>
      4 #include <stddef.h>
      5 #include <stdint.h>
      6 #include <stdio.h>
      7 #include <string.h>
      8 
      9 #include "dist_host.h"
     10 #include "driver.h"
     11 #include "env.h"
     12 
     13 #define PKG_TOOL "pkg"
     14 #define PKG_PATH_BUF 1024u
     15 #define PKG_TRUST_LINE_MAX 1024u
     16 #define PKG_KEYID_HEX (2u * KIT_PKG_KEYID_LEN + 1u)
     17 
     18 void driver_help_pkg(void) {
     19   driver_printf(
     20       "kit pkg - signed code distribution\n"
     21       "\n"
     22       "USAGE\n"
     23       "  kit pkg keygen -o BASE\n"
     24       "  kit pkg create --name N --version V [--desc D] -s SECKEY\n"
     25       "                   [--format kpkg|tar.gz] [--compression "
     26       "none|lz4-block-v1]\n"
     27       "                   [--native-shape fat|metadata|thin] [--external DIR]\n"
     28       "                   (--cas DIR --tree TREE_ID | --root DIR) -o OUT\n"
     29       "  kit pkg verify [-p PUBKEY | --tofu] [--format kpkg|tar.gz]\n"
     30       "                   [--external DIR] FILE\n"
     31       "  kit pkg unpack [--verify] [-p PUBKEY | --tofu] [--format "
     32       "kpkg|tar.gz]\n"
     33       "                   [--external DIR] FILE -C DIR\n"
     34       "  kit pkg inspect [--manifest | --encoding] FILE\n"
     35       "  kit pkg trust {path | list | add PUBKEY [label] | remove KEYID}\n");
     36 }
     37 
     38 /* ---------------------------------------------------------------------- */
     39 /* small driver-side helpers                                              */
     40 /* ---------------------------------------------------------------------- */
     41 
     42 static int pkg_read(const KitContext* ctx, const char* path, KitFileData* out) {
     43   out->data = NULL;
     44   out->size = 0;
     45   out->token = NULL;
     46   return ctx->file_io->read_all(ctx->file_io->user, path, out) == KIT_OK;
     47 }
     48 
     49 static void pkg_release(const KitContext* ctx, KitFileData* fd) {
     50   if ((fd->token || fd->data) && ctx->file_io->release)
     51     ctx->file_io->release(ctx->file_io->user, fd);
     52 }
     53 
     54 static void pkg_parent_dir(const char* path, char* buf, size_t cap) {
     55   const char* slash = NULL;
     56   const char* p;
     57   size_t n;
     58   for (p = path; *p; ++p)
     59     if (*p == '/') slash = p;
     60   if (!slash) {
     61     buf[0] = '\0';
     62     return;
     63   }
     64   n = (size_t)(slash - path);
     65   if (n >= cap) n = cap - 1u;
     66   memcpy(buf, path, n);
     67   buf[n] = '\0';
     68 }
     69 
     70 static int pkg_trust_path(char* buf, size_t cap) {
     71   const char* env = driver_getenv("KIT_TRUSTED_KEYS");
     72   const char* home;
     73   if (env) {
     74     snprintf(buf, cap, "%s", env);
     75     return 0;
     76   }
     77   home = driver_getenv("HOME");
     78   if (!home) return 1;
     79   snprintf(buf, cap, "%s/.config/kit/trusted_keys", home);
     80   return 0;
     81 }
     82 
     83 static KitPkgFormat pkg_parse_format(const char* s) {
     84   if (driver_streq(s, "kpkg") || driver_streq(s, "native"))
     85     return KIT_PKG_FORMAT_KPKG;
     86   if (driver_streq(s, "tar.gz") || driver_streq(s, "portable"))
     87     return KIT_PKG_FORMAT_TARGZ;
     88   return KIT_PKG_FORMAT_AUTO;
     89 }
     90 
     91 static KitPkgFormat pkg_infer_format(const char* path) {
     92   if (driver_has_suffix(path, ".tar.gz")) return KIT_PKG_FORMAT_TARGZ;
     93   if (driver_has_suffix(path, ".kpkg")) return KIT_PKG_FORMAT_KPKG;
     94   return KIT_PKG_FORMAT_AUTO;
     95 }
     96 
     97 static int pkg_parse_native_shape(const char* s, KitPkgShape* out) {
     98   if (driver_streq(s, "fat")) {
     99     *out = KIT_PKG_SHAPE_FAT;
    100     return 0;
    101   }
    102   if (driver_streq(s, "metadata") || driver_streq(s, "metadata-rich")) {
    103     *out = KIT_PKG_SHAPE_METADATA;
    104     return 0;
    105   }
    106   if (driver_streq(s, "thin")) {
    107     *out = KIT_PKG_SHAPE_THIN;
    108     return 0;
    109   }
    110   return 1;
    111 }
    112 
    113 static int pkg_parse_compression(const char* s, KitPkgCompression* out) {
    114   if (driver_streq(s, "none")) {
    115     *out = KIT_PKG_COMPRESSION_NONE;
    116     return 0;
    117   }
    118   if (driver_streq(s, "lz4-block-v1") || driver_streq(s, "lz4")) {
    119     *out = KIT_PKG_COMPRESSION_LZ4_BLOCK_V1;
    120     return 0;
    121   }
    122   return 1;
    123 }
    124 
    125 /* ---------------------------------------------------------------------- */
    126 /* keygen                                                                 */
    127 /* ---------------------------------------------------------------------- */
    128 
    129 static int pkg_keygen(DriverEnv* env, const KitContext* ctx, int argc,
    130                       char** argv) {
    131   const char* base = NULL;
    132   KitWriter *pubw = NULL, *seckw = NULL;
    133   uint8_t keyid[KIT_PKG_KEYID_LEN];
    134   char path[PKG_PATH_BUF], hex[PKG_KEYID_HEX];
    135   int i, rc = 1;
    136   (void)env;
    137   for (i = 0; i < argc; ++i) {
    138     if (driver_streq(argv[i], "-o") && i + 1 < argc)
    139       base = argv[++i];
    140     else {
    141       driver_errf(PKG_TOOL, "keygen: unexpected argument: %s", argv[i]);
    142       return 2;
    143     }
    144   }
    145   if (!base) {
    146     driver_errf(PKG_TOOL, "keygen: -o BASE is required");
    147     return 2;
    148   }
    149   snprintf(path, sizeof path, "%s.pub", base);
    150   if (ctx->file_io->open_writer(ctx->file_io->user, path, &pubw) != KIT_OK) {
    151     driver_errf(PKG_TOOL, "keygen: failed to open output: %s", path);
    152     return 1;
    153   }
    154   snprintf(path, sizeof path, "%s.key", base);
    155   if (ctx->file_io->open_writer(ctx->file_io->user, path, &seckw) != KIT_OK) {
    156     driver_errf(PKG_TOOL, "keygen: failed to open output: %s", path);
    157     kit_writer_close(pubw);
    158     return 1;
    159   }
    160   if (kit_pkg_keygen(ctx, driver_dist_random, NULL, pubw, seckw, keyid) ==
    161       KIT_OK)
    162     rc = 0;
    163   kit_writer_close(seckw);
    164   kit_writer_close(pubw);
    165   if (rc == 0) {
    166     kit_hex_encode(hex, keyid, KIT_PKG_KEYID_LEN);
    167     driver_printf("wrote %s.pub and %s.key (key id %s)\n", base, base, hex);
    168   }
    169   return rc;
    170 }
    171 
    172 /* ---------------------------------------------------------------------- */
    173 /* create                                                                 */
    174 /* ---------------------------------------------------------------------- */
    175 
    176 static int pkg_create(DriverEnv* env, const KitContext* ctx, int argc,
    177                       char** argv) {
    178   KitPkgCreateOptions opts;
    179   KitPkgCreateResult result;
    180   KitCasHost host;
    181   KitFileData skfd;
    182   uint8_t sk[KIT_PKG_SK_LEN], keyid[KIT_PKG_KEYID_LEN];
    183   const char* seckey = NULL;
    184   char pkgid_hex[2 * KIT_CAS_HASH_LEN + 1];
    185   int i, rc = 1, sk_loaded = 0;
    186 
    187   memset(&opts, 0, sizeof opts);
    188   opts.format = KIT_PKG_FORMAT_AUTO;
    189   opts.native_shape = KIT_PKG_SHAPE_FAT;
    190   opts.compression = KIT_PKG_COMPRESSION_NONE;
    191   for (i = 0; i < argc; ++i) {
    192     const char* a = argv[i];
    193     if (driver_streq(a, "--name") && i + 1 < argc)
    194       opts.name = argv[++i];
    195     else if (driver_streq(a, "--version") && i + 1 < argc)
    196       opts.version = argv[++i];
    197     else if (driver_streq(a, "--desc") && i + 1 < argc)
    198       opts.description = argv[++i];
    199     else if (driver_streq(a, "-o") && i + 1 < argc)
    200       opts.out_path = argv[++i];
    201     else if (driver_streq(a, "-s") && i + 1 < argc)
    202       seckey = argv[++i];
    203     else if (driver_streq(a, "--cas") && i + 1 < argc)
    204       opts.cas_dir = argv[++i];
    205     else if (driver_streq(a, "--tree") && i + 1 < argc)
    206       opts.tree_id = argv[++i];
    207     else if (driver_streq(a, "--root") && i + 1 < argc)
    208       opts.root_dir = argv[++i];
    209     else if (driver_streq(a, "--external") && i + 1 < argc)
    210       opts.external_dir = argv[++i];
    211     else if (driver_streq(a, "--native-shape") && i + 1 < argc) {
    212       if (pkg_parse_native_shape(argv[++i], &opts.native_shape) != 0) {
    213         driver_errf(PKG_TOOL, "create: unknown native shape");
    214         return 2;
    215       }
    216     } else if (driver_streq(a, "--format") && i + 1 < argc) {
    217       opts.format = pkg_parse_format(argv[++i]);
    218       if (opts.format == KIT_PKG_FORMAT_AUTO) {
    219         driver_errf(PKG_TOOL, "create: unknown format");
    220         return 2;
    221       }
    222     } else if (driver_streq(a, "--compression") && i + 1 < argc) {
    223       if (pkg_parse_compression(argv[++i], &opts.compression) != 0) {
    224         driver_errf(PKG_TOOL, "create: unknown compression");
    225         return 2;
    226       }
    227     } else if (a[0] == '-' && a[1] != '\0') {
    228       driver_errf(PKG_TOOL, "create: unknown option: %s", a);
    229       return 2;
    230     } else {
    231       driver_errf(
    232           PKG_TOOL,
    233           "create: positional file inputs were removed; use --root DIR");
    234       return 2;
    235     }
    236   }
    237   if (!opts.name || !opts.version || !opts.out_path || !seckey) {
    238     driver_errf(PKG_TOOL,
    239                 "create: --name, --version, -s SECKEY and -o OUT are required");
    240     return 2;
    241   }
    242   if ((opts.root_dir != NULL) ==
    243           (opts.cas_dir != NULL || opts.tree_id != NULL) ||
    244       (opts.cas_dir && !opts.tree_id) || (opts.tree_id && !opts.cas_dir)) {
    245     driver_errf(
    246         PKG_TOOL,
    247         "create: pass exactly one of --root DIR or --cas DIR --tree TREE_ID");
    248     return 2;
    249   }
    250   if (opts.format == KIT_PKG_FORMAT_AUTO)
    251     opts.format = pkg_infer_format(opts.out_path);
    252   if (opts.format == KIT_PKG_FORMAT_AUTO) {
    253     driver_errf(PKG_TOOL, "create: cannot infer format; pass --format");
    254     return 2;
    255   }
    256   if (opts.format != KIT_PKG_FORMAT_KPKG &&
    257       opts.native_shape != KIT_PKG_SHAPE_FAT) {
    258     driver_errf(PKG_TOOL, "create: --native-shape only applies to kpkg");
    259     return 2;
    260   }
    261   if (opts.format != KIT_PKG_FORMAT_KPKG && opts.external_dir) {
    262     driver_errf(PKG_TOOL, "create: --external only applies to kpkg");
    263     return 2;
    264   }
    265 
    266   if (!pkg_read(ctx, seckey, &skfd)) {
    267     driver_errf(PKG_TOOL, "create: cannot read secret key: %s", seckey);
    268     return 1;
    269   }
    270   sk_loaded = 1;
    271   {
    272     KitStatus kr = kit_minisig_parse_seckey(skfd.data, skfd.size, sk, keyid);
    273     if (kr == KIT_UNSUPPORTED)
    274       driver_errf(PKG_TOOL, "create: encrypted secret keys need scrypt");
    275     else if (kr != KIT_OK)
    276       driver_errf(PKG_TOOL, "create: malformed secret key: %s", seckey);
    277     if (kr != KIT_OK) goto done;
    278   }
    279   opts.sk = sk;
    280   opts.keyid = keyid;
    281 
    282   host = driver_cas_host(env);
    283   if (kit_pkg_create(ctx, &host, &opts, &result) == KIT_OK) {
    284     kit_hex_encode(pkgid_hex, result.package_id, KIT_CAS_HASH_LEN);
    285     driver_printf("wrote %s (%llu file(s), id %s)\n", opts.out_path,
    286                   (unsigned long long)result.n_files, pkgid_hex);
    287     rc = 0;
    288   }
    289 
    290 done:
    291   if (sk_loaded) pkg_release(ctx, &skfd);
    292   return rc;
    293 }
    294 
    295 /* ---------------------------------------------------------------------- */
    296 /* verify / unpack                                                        */
    297 /* ---------------------------------------------------------------------- */
    298 
    299 /* Persist a trust-on-first-use pin. Returns 0 on success; nonzero if it could
    300  * not be written (no trust path, or I/O failure) — the original tool failed
    301  * verification when the pin could not be persisted, so the caller treats a
    302  * nonzero return as a verify failure. */
    303 static int pkg_pin_tofu(DriverEnv* env, const KitContext* ctx,
    304                         const char* tpath, const KitPkgVerifyResult* r) {
    305   char line[PKG_TRUST_LINE_MAX], parent[PKG_PATH_BUF], hex[PKG_KEYID_HEX];
    306   KitFileData old;
    307   KitWriter* w = NULL;
    308   int had_old, ok = 1, wrote = 0;
    309   if (!tpath[0]) return 1;
    310   kit_hex_encode(hex, r->keyid, KIT_PKG_KEYID_LEN);
    311   if (kit_trust_format_entry(line, sizeof line, r->keyid, r->tofu_pk,
    312                              "tofu-pinned") != KIT_OK)
    313     return 1;
    314   had_old = pkg_read(ctx, tpath, &old);
    315   pkg_parent_dir(tpath, parent, sizeof parent);
    316   if (parent[0]) driver_mkdir_p(env, parent);
    317   if (ctx->file_io->open_writer(ctx->file_io->user, tpath, &w) == KIT_OK) {
    318     if (had_old && old.size)
    319       ok = kit_writer_write(w, old.data, old.size) == KIT_OK;
    320     if (ok) ok = kit_writer_write(w, line, strlen(line)) == KIT_OK;
    321     if (ok && kit_writer_status(w) == KIT_OK) wrote = 1;
    322     kit_writer_close(w);
    323   }
    324   if (had_old) pkg_release(ctx, &old);
    325   if (wrote) driver_printf("pkg: tofu-pinned key id %s to %s\n", hex, tpath);
    326   return wrote ? 0 : 1;
    327 }
    328 
    329 static int pkg_verify_or_unpack(DriverEnv* env, const KitContext* ctx, int argc,
    330                                 char** argv, int unpack) {
    331   const char *file = NULL, *pubkey = NULL, *dir = ".", *external_dir = NULL;
    332   int tofu = 0, explicit_verify = 0, i, rc = 1;
    333   KitPkgFormat fmt = KIT_PKG_FORMAT_AUTO;
    334   KitPkgVerifyOptions opts;
    335   KitPkgVerifyResult result;
    336   KitCasHost host;
    337   KitFileData pkgfd, pubfd, trustfd;
    338   char tpath[PKG_PATH_BUF];
    339   int pub_loaded = 0, trust_loaded = 0, have_tpath;
    340   for (i = 0; i < argc; ++i) {
    341     if (driver_streq(argv[i], "-p") && i + 1 < argc)
    342       pubkey = argv[++i];
    343     else if (driver_streq(argv[i], "--tofu"))
    344       tofu = 1;
    345     else if (unpack && driver_streq(argv[i], "--verify"))
    346       explicit_verify = 1;
    347     else if (driver_streq(argv[i], "--external") && i + 1 < argc)
    348       external_dir = argv[++i];
    349     else if (driver_streq(argv[i], "--format") && i + 1 < argc) {
    350       fmt = pkg_parse_format(argv[++i]);
    351       if (fmt == KIT_PKG_FORMAT_AUTO) {
    352         driver_errf(PKG_TOOL, "%s: unknown format",
    353                     unpack ? "unpack" : "verify");
    354         return 2;
    355       }
    356     } else if (unpack && driver_streq(argv[i], "-C") && i + 1 < argc)
    357       dir = argv[++i];
    358     else if (argv[i][0] != '-')
    359       file = argv[i];
    360     else {
    361       driver_errf(PKG_TOOL, "%s: unknown option: %s",
    362                   unpack ? "unpack" : "verify", argv[i]);
    363       return 2;
    364     }
    365   }
    366   if (!file) {
    367     driver_errf(PKG_TOOL, "%s: FILE is required", unpack ? "unpack" : "verify");
    368     return 2;
    369   }
    370   if (fmt == KIT_PKG_FORMAT_AUTO) fmt = pkg_infer_format(file);
    371 
    372   if (!pkg_read(ctx, file, &pkgfd)) {
    373     driver_errf(PKG_TOOL, "cannot read package: %s", file);
    374     return 1;
    375   }
    376   memset(&opts, 0, sizeof opts);
    377   opts.pkg_data = pkgfd.data;
    378   opts.pkg_len = pkgfd.size;
    379   opts.format = fmt;
    380   opts.external_dir = external_dir;
    381   opts.unpack_dir = unpack ? dir : NULL;
    382   opts.tofu = tofu;
    383   if (pubkey) {
    384     if (!pkg_read(ctx, pubkey, &pubfd)) {
    385       driver_errf(PKG_TOOL, "cannot read public key: %s", pubkey);
    386       pkg_release(ctx, &pkgfd);
    387       return 1;
    388     }
    389     pub_loaded = 1;
    390     opts.pubkey_bytes = pubfd.data;
    391     opts.pubkey_len = pubfd.size;
    392   }
    393   have_tpath = (pkg_trust_path(tpath, sizeof tpath) == 0);
    394   if (!have_tpath) tpath[0] = '\0';
    395   if (have_tpath && !pubkey && pkg_read(ctx, tpath, &trustfd)) {
    396     trust_loaded = 1;
    397     opts.trusted_keys = trustfd.data;
    398     opts.trusted_keys_len = trustfd.size;
    399   }
    400 
    401   host = driver_cas_host(env);
    402   if (kit_pkg_verify(ctx, &host, &opts, &result) == KIT_OK) {
    403     /* A trust-on-first-use acceptance must persist its pin, like the original
    404      * tool — a pin that can't be written fails verification (exit 1). */
    405     if (result.tofu_pin && pkg_pin_tofu(env, ctx, tpath, &result) != 0) {
    406       driver_errf(PKG_TOOL,
    407                   "tofu: could not persist key id to trusted-keys "
    408                   "(set KIT_TRUSTED_KEYS or HOME)");
    409     } else {
    410       int quiet = unpack && !explicit_verify;
    411       if (!quiet) {
    412         char idhex[PKG_KEYID_HEX];
    413         kit_hex_encode(idhex, result.keyid, KIT_PKG_KEYID_LEN);
    414         driver_printf("ok: %s %s  signer %s  [%s]\n", result.name,
    415                       result.version, idhex, result.trusted);
    416       }
    417       if (opts.unpack_dir)
    418         driver_printf("unpacked %s %s to %s\n", result.name, result.version,
    419                       opts.unpack_dir);
    420       rc = 0;
    421     }
    422   }
    423 
    424   if (trust_loaded) pkg_release(ctx, &trustfd);
    425   if (pub_loaded) pkg_release(ctx, &pubfd);
    426   pkg_release(ctx, &pkgfd);
    427   return rc;
    428 }
    429 
    430 /* ---------------------------------------------------------------------- */
    431 /* inspect                                                                */
    432 /* ---------------------------------------------------------------------- */
    433 
    434 static int pkg_inspect(DriverEnv* env, const KitContext* ctx, int argc,
    435                        char** argv) {
    436   const char* file = NULL;
    437   KitPkgFormat fmt = KIT_PKG_FORMAT_AUTO;
    438   int show_encoding = 0, i, rc = 1;
    439   KitFileData fd;
    440   KitWriter* out;
    441   for (i = 0; i < argc; ++i) {
    442     if (driver_streq(argv[i], "--format") && i + 1 < argc) {
    443       fmt = pkg_parse_format(argv[++i]);
    444       if (fmt == KIT_PKG_FORMAT_AUTO) {
    445         driver_errf(PKG_TOOL, "inspect: unknown format");
    446         return 2;
    447       }
    448     } else if (driver_streq(argv[i], "--manifest")) {
    449       show_encoding = 0;
    450     } else if (driver_streq(argv[i], "--encoding")) {
    451       show_encoding = 1;
    452     } else if (argv[i][0] != '-')
    453       file = argv[i];
    454     else {
    455       driver_errf(PKG_TOOL, "inspect: unknown option: %s", argv[i]);
    456       return 2;
    457     }
    458   }
    459   if (!file) {
    460     driver_errf(PKG_TOOL, "inspect: FILE is required");
    461     return 2;
    462   }
    463   if (fmt == KIT_PKG_FORMAT_AUTO) fmt = pkg_infer_format(file);
    464   if (fmt == KIT_PKG_FORMAT_TARGZ && show_encoding) {
    465     driver_errf(PKG_TOOL,
    466                 "inspect: portable packages have no encoding descriptor");
    467     return 2;
    468   }
    469   if (!pkg_read(ctx, file, &fd)) {
    470     driver_errf(PKG_TOOL, "cannot read package: %s", file);
    471     return 1;
    472   }
    473   out = driver_stdout_writer(env);
    474   if (out) {
    475     if (kit_pkg_inspect(ctx, fd.data, fd.size, fmt, show_encoding, out) ==
    476         KIT_OK)
    477       rc = 0;
    478     kit_writer_close(out);
    479   }
    480   pkg_release(ctx, &fd);
    481   return rc;
    482 }
    483 
    484 /* ---------------------------------------------------------------------- */
    485 /* trust                                                                  */
    486 /* ---------------------------------------------------------------------- */
    487 
    488 static int pkg_trust(DriverEnv* env, const KitContext* ctx, int argc,
    489                      char** argv) {
    490   char tpath[PKG_PATH_BUF];
    491   const char* sub = (argc > 0) ? argv[0] : "list";
    492   if (pkg_trust_path(tpath, sizeof tpath) != 0) {
    493     driver_errf(PKG_TOOL,
    494                 "no trusted-keys path (set KIT_TRUSTED_KEYS or HOME)");
    495     return 1;
    496   }
    497   if (driver_streq(sub, "path")) {
    498     if (argc > 1) {
    499       driver_errf(PKG_TOOL, "trust path: unexpected argument: %s", argv[1]);
    500       return 2;
    501     }
    502     driver_printf("%s\n", tpath);
    503     return 0;
    504   }
    505   if (driver_streq(sub, "list")) {
    506     KitFileData fd;
    507     if (!pkg_read(ctx, tpath, &fd)) {
    508       driver_printf("(no trusted keys at %s)\n", tpath);
    509       return 0;
    510     }
    511     driver_printf("%.*s", (int)fd.size, (const char*)fd.data);
    512     pkg_release(ctx, &fd);
    513     return 0;
    514   }
    515   if (driver_streq(sub, "add")) {
    516     const char* pubkey = (argc > 1) ? argv[1] : NULL;
    517     const char* label = (argc > 2) ? argv[2] : "";
    518     KitFileData kf, old;
    519     uint8_t pk[KIT_PKG_PK_LEN], keyid[KIT_PKG_KEYID_LEN], dummy[KIT_PKG_PK_LEN];
    520     char line[PKG_TRUST_LINE_MAX], parent[PKG_PATH_BUF];
    521     int had_old, ok = 1, rc = 1;
    522     KitWriter* w = NULL;
    523     if (!pubkey) {
    524       driver_errf(PKG_TOOL, "trust add: PUBKEY is required");
    525       return 2;
    526     }
    527     if (!pkg_read(ctx, pubkey, &kf)) {
    528       driver_errf(PKG_TOOL, "cannot read public key: %s", pubkey);
    529       return 1;
    530     }
    531     ok = kit_minisig_parse_pubkey(kf.data, kf.size, pk, keyid) == KIT_OK;
    532     pkg_release(ctx, &kf);
    533     if (!ok) return 1;
    534     had_old = pkg_read(ctx, tpath, &old);
    535     if (had_old &&
    536         kit_trust_lookup(old.data, old.size, keyid, dummy) == KIT_OK) {
    537       pkg_release(ctx, &old);
    538       return 0;
    539     }
    540     if (kit_trust_format_entry(line, sizeof line, keyid, pk, label) != KIT_OK) {
    541       if (had_old) pkg_release(ctx, &old);
    542       return 1;
    543     }
    544     pkg_parent_dir(tpath, parent, sizeof parent);
    545     if (parent[0]) driver_mkdir_p(env, parent);
    546     if (ctx->file_io->open_writer(ctx->file_io->user, tpath, &w) == KIT_OK) {
    547       if (had_old && old.size)
    548         ok = kit_writer_write(w, old.data, old.size) == KIT_OK;
    549       if (ok) ok = kit_writer_write(w, line, strlen(line)) == KIT_OK;
    550       if (ok && kit_writer_status(w) == KIT_OK) rc = 0;
    551       kit_writer_close(w);
    552     }
    553     if (had_old) pkg_release(ctx, &old);
    554     return rc;
    555   }
    556   if (driver_streq(sub, "remove")) {
    557     const char* idhex = (argc > 1) ? argv[1] : NULL;
    558     KitFileData old;
    559     uint8_t want[KIT_PKG_KEYID_LEN];
    560     KitWriter* w = NULL;
    561     size_t pos = 0;
    562     int rc = 1;
    563     if (!idhex || strlen(idhex) != 2 * KIT_PKG_KEYID_LEN ||
    564         kit_hex_decode(want, idhex, KIT_PKG_KEYID_LEN) != KIT_OK)
    565       return 2;
    566     if (!pkg_read(ctx, tpath, &old)) return 0;
    567     if (ctx->file_io->open_writer(ctx->file_io->user, tpath, &w) != KIT_OK) {
    568       pkg_release(ctx, &old);
    569       return 1;
    570     }
    571     rc = 0;
    572     while (pos < old.size) {
    573       size_t start = pos, end = pos;
    574       uint8_t got[KIT_PKG_KEYID_LEN];
    575       char idbuf[2 * KIT_PKG_KEYID_LEN + 1];
    576       int keep = 1;
    577       while (end < old.size && old.data[end] != '\n') ++end;
    578       if (end - start >= 2 * KIT_PKG_KEYID_LEN) {
    579         memcpy(idbuf, old.data + start, 2 * KIT_PKG_KEYID_LEN);
    580         idbuf[2 * KIT_PKG_KEYID_LEN] = '\0';
    581         if (kit_hex_decode(got, idbuf, KIT_PKG_KEYID_LEN) == KIT_OK &&
    582             memcmp(got, want, KIT_PKG_KEYID_LEN) == 0)
    583           keep = 0;
    584       }
    585       if (keep) {
    586         size_t n = (end < old.size ? end + 1 : end) - start;
    587         if (kit_writer_write(w, old.data + start, n) != KIT_OK) rc = 1;
    588       }
    589       pos = (end < old.size) ? end + 1 : end;
    590     }
    591     if (kit_writer_status(w) != KIT_OK) rc = 1;
    592     kit_writer_close(w);
    593     pkg_release(ctx, &old);
    594     return rc;
    595   }
    596   driver_errf(PKG_TOOL, "trust: unknown subcommand: %s", sub);
    597   return 2;
    598 }
    599 
    600 int driver_pkg(int argc, char** argv) {
    601   DriverEnv env;
    602   KitContext ctx;
    603   const char* sub;
    604   int rc;
    605   if (driver_argv_wants_help(argc, argv, 1) || argc < 2) {
    606     driver_help_pkg();
    607     return argc < 2 ? 2 : 0;
    608   }
    609   sub = argv[1];
    610   driver_env_init(&env);
    611   ctx = driver_env_to_context(&env);
    612   if (driver_streq(sub, "keygen"))
    613     rc = pkg_keygen(&env, &ctx, argc - 2, argv + 2);
    614   else if (driver_streq(sub, "create"))
    615     rc = pkg_create(&env, &ctx, argc - 2, argv + 2);
    616   else if (driver_streq(sub, "verify"))
    617     rc = pkg_verify_or_unpack(&env, &ctx, argc - 2, argv + 2, 0);
    618   else if (driver_streq(sub, "unpack"))
    619     rc = pkg_verify_or_unpack(&env, &ctx, argc - 2, argv + 2, 1);
    620   else if (driver_streq(sub, "inspect"))
    621     rc = pkg_inspect(&env, &ctx, argc - 2, argv + 2);
    622   else if (driver_streq(sub, "trust"))
    623     rc = pkg_trust(&env, &ctx, argc - 2, argv + 2);
    624   else {
    625     driver_errf(PKG_TOOL, "unknown subcommand: %s", sub);
    626     rc = 2;
    627   }
    628   driver_env_fini(&env);
    629   return rc;
    630 }