kit

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

run.sh (31122B)


      1 #!/bin/sh
      2 # Driver-level checks for kit pkg distribution formats.
      3 
      4 set -u
      5 
      6 script_dir=$(cd "$(dirname "$0")" && pwd)
      7 repo_root=$(cd "$script_dir/../.." && pwd)
      8 
      9 KIT="${KIT:-$repo_root/build/kit}"
     10 
     11 if [ ! -x "$KIT" ]; then
     12     echo "pkg: kit binary not found at $KIT" >&2
     13     exit 2
     14 fi
     15 
     16 work=$(mktemp -d "${TMPDIR:-/tmp}/kit-pkg-test.XXXXXX")
     17 trap 'rm -rf "$work"' EXIT
     18 
     19 HOME="$work/home"
     20 KIT_TRUSTED_KEYS="$work/trusted_keys"
     21 SOURCE_DATE_EPOCH=1
     22 export HOME KIT_TRUSTED_KEYS SOURCE_DATE_EPOCH
     23 mkdir -p "$HOME" "$work/in" "$work/pkg" "$work/unpack" "$work/cas"
     24 
     25 artifacts="empty.dat one.bin dup-one.bin payload.txt chunk64.bin chunk64-copy.bin chunk64p1.bin chunk3.bin bin/tool.sh"
     26 tree_id=
     27 
     28 # Type-K mode-P kit: shared assert verbs + CAS helpers, all recording through
     29 # the unified kit_* counters over $work. pkg-specific helpers (not_contains,
     30 # same_artifacts, not_same_file, have_cmd) stay below.
     31 KIT_KIT_DIR="$repo_root/test/lib"
     32 . "$repo_root/test/lib/kit_sh_kit.sh"
     33 kit_report_init
     34 
     35 not_contains() {
     36     name=$1
     37     file=$2
     38     needle=$3
     39     if grep -F "$needle" "$file" >/dev/null 2>&1; then
     40         {
     41             printf 'unexpected text: %s\n' "$needle"
     42             sed 's/^/file: /' "$file"
     43         } > "$work/$name.diag"
     44         not_ok "$name" "$work/$name.diag"
     45     else
     46         ok "$name"
     47     fi
     48 }
     49 
     50 same_artifacts() {
     51     name=$1
     52     dir=$2
     53     diag="$work/$name.diag"
     54     : > "$diag"
     55     good=1
     56     for f in $artifacts; do
     57         if ! cmp -s "$work/in/$f" "$dir/$f"; then
     58             printf 'artifact differs or is missing: %s\n' "$f" >> "$diag"
     59             good=0
     60         fi
     61     done
     62     if [ "$good" -eq 1 ]; then
     63         ok "$name"
     64     else
     65         not_ok "$name" "$diag"
     66     fi
     67 }
     68 
     69 not_same_file() {
     70     name=$1
     71     left=$2
     72     right=$3
     73     if cmp -s "$left" "$right"; then
     74         {
     75             printf 'files unexpectedly match:\n'
     76             printf '  left:  %s\n' "$left"
     77             printf '  right: %s\n' "$right"
     78         } > "$work/$name.diag"
     79         not_ok "$name" "$work/$name.diag"
     80     else
     81         ok "$name"
     82     fi
     83 }
     84 
     85 have_cmd() {
     86     command -v "$1" >/dev/null 2>&1
     87 }
     88 
     89 make_fixtures() {
     90     : > "$work/in/empty.dat"
     91     printf x > "$work/in/one.bin"
     92     cp "$work/in/one.bin" "$work/in/dup-one.bin"
     93     printf 'portable package deflate regression payload\n' > "$work/in/payload.txt"
     94     dd if=/dev/zero of="$work/in/chunk64.bin" bs=65536 count=1 >/dev/null 2>&1
     95     cp "$work/in/chunk64.bin" "$work/in/chunk64-copy.bin"
     96     dd if=/dev/zero of="$work/in/chunk64p1.bin" bs=65536 count=1 >/dev/null 2>&1
     97     printf y >> "$work/in/chunk64p1.bin"
     98     dd if=/dev/zero of="$work/in/chunk3.bin" bs=65536 count=3 >/dev/null 2>&1
     99     printf tail >> "$work/in/chunk3.bin"
    100     mkdir -p "$work/in/bin"
    101     {
    102         printf '#!/bin/sh\n'
    103         printf 'printf "tool executed\\n"\n'
    104     } > "$work/in/bin/tool.sh"
    105     chmod +x "$work/in/bin/tool.sh"
    106 }
    107 
    108 keyid_from_keygen() {
    109     sed -n 's/.*key id \([0-9a-fA-F][0-9a-fA-F]*\)).*/\1/p' "$1"
    110 }
    111 
    112 inspect_checks() {
    113     label=$1
    114     out=$2
    115     contains "$label-inspect-magic" "$out" "kit-package 3"
    116     contains "$label-inspect-name" "$out" "name = matrix-test"
    117     contains "$label-inspect-version" "$out" "version = 1.0.0"
    118     contains "$label-inspect-desc" "$out" "description = package test matrix"
    119     contains "$label-inspect-hash" "$out" "hash = blake2b-256"
    120     contains "$label-inspect-tree-format" "$out" "tree = kit-tree-v1"
    121     contains "$label-inspect-blob-format" "$out" "blob = kit-blob-v1"
    122     contains "$label-inspect-output" "$out" "[output]"
    123     contains "$label-inspect-output-tree" "$out" "tree = $tree_id"
    124     contains "$label-inspect-empty" "$out" "path = empty.dat"
    125     contains "$label-inspect-boundary" "$out" "path = chunk64p1.bin"
    126     contains "$label-inspect-exec" "$out" "path = bin/tool.sh"
    127     not_contains "$label-inspect-no-v2-hash" "$out" "blake2b-merkle-v1"
    128     not_contains "$label-inspect-no-sha256" "$out" "sha256"
    129 }
    130 
    131 repack_targz() {
    132     src=$1
    133     out=$2
    134     files=$(cd "$src" && find . -type f -print | sed 's#^\./##' | sort)
    135     (cd "$src" && tar -czf "$out" $files)
    136 }
    137 
    138 extract_targz() {
    139     pkg=$1
    140     dst=$2
    141     mkdir -p "$dst"
    142     gzip -dc "$pkg" | tar -xf - -C "$dst"
    143 }
    144 
    145 flip_byte() {
    146     file=$1
    147     off=$2
    148     printf '\377' | dd of="$file" bs=1 seek="$off" count=1 conv=notrunc >/dev/null 2>&1
    149 }
    150 
    151 kpkg3_field_offset() {
    152     file=$1
    153     idx=$2
    154     perl -e '
    155         use strict;
    156         use warnings;
    157         my ($file, $idx) = @ARGV;
    158         open my $fh, "<:raw", $file or die "$file: $!";
    159         read $fh, my $magic, 7;
    160         die "bad magic" unless $magic eq "kpkg3\0\0";
    161         seek $fh, 16 + 8 * $idx, 0 or die "seek: $!";
    162         read $fh, my $b, 8;
    163         die "short read" unless length($b) == 8;
    164         print unpack("Q<", $b);
    165     ' "$file" "$idx"
    166 }
    167 
    168 extract_kpkg3_descriptor() {
    169     pkg=$1
    170     out=$2
    171     descriptor_off=$(kpkg3_field_offset "$pkg" 4)
    172     descriptor_size=$(kpkg3_field_offset "$pkg" 5)
    173     dd if="$pkg" of="$out" bs=1 skip="$descriptor_off" count="$descriptor_size" >/dev/null 2>&1
    174 }
    175 
    176 extract_kpkg3_manifest() {
    177     pkg=$1
    178     out=$2
    179     manifest_off=$(kpkg3_field_offset "$pkg" 0)
    180     manifest_size=$(kpkg3_field_offset "$pkg" 1)
    181     dd if="$pkg" of="$out" bs=1 skip="$manifest_off" count="$manifest_size" >/dev/null 2>&1
    182 }
    183 
    184 descriptor_value() {
    185     file=$1
    186     key=$2
    187     sed -n "s/^$key = //p" "$file" | sed -n '1p'
    188 }
    189 
    190 check_kpkg3_index() {
    191     label=$1
    192     pkg=$2
    193     descriptor=$3
    194     expected_compression=$4
    195 
    196     index_off=$(descriptor_value "$descriptor" "index-offset")
    197     index_size=$(descriptor_value "$descriptor" "index-size")
    198     content_size=$(descriptor_value "$descriptor" "content-size")
    199     empty_blob=$(tree_blob_for_path "$tree_file" "empty.dat")
    200     one_blob=$(tree_blob_for_path "$tree_file" "one.bin")
    201     dup_one_blob=$(tree_blob_for_path "$tree_file" "dup-one.bin")
    202     chunk64_blob=$(tree_blob_for_path "$tree_file" "chunk64.bin")
    203     chunk64_copy_blob=$(tree_blob_for_path "$tree_file" "chunk64-copy.bin")
    204     chunk64p1_blob=$(tree_blob_for_path "$tree_file" "chunk64p1.bin")
    205     chunk3_blob=$(tree_blob_for_path "$tree_file" "chunk3.bin")
    206     if perl -e '
    207         use strict;
    208         use warnings;
    209         my ($pkg, $index_off, $index_size, $content_size, $expected_comp,
    210             $empty, $one, $dup_one, $chunk64, $chunk64_copy, $chunk64p1,
    211             $chunk3) = @ARGV;
    212         die "duplicate one.bin blob mismatch" unless $one eq $dup_one;
    213         die "duplicate chunk64 blob mismatch" unless $chunk64 eq $chunk64_copy;
    214         die "bad index size" unless $index_size % 168 == 0;
    215         open my $fh, "<:raw", $pkg or die "$pkg: $!";
    216         seek $fh, $index_off, 0 or die "seek: $!";
    217         read $fh, my $index, $index_size;
    218         die "short index" unless length($index) == $index_size;
    219         my %count;
    220         my ($prev_blob, $prev_chunk) = ("", -1);
    221         for (my $off = 0; $off < $index_size; $off += 168) {
    222             my $rec = substr($index, $off, 168);
    223             my $blob = unpack("H64", substr($rec, 0, 32));
    224             my $chunk = unpack("Q<", substr($rec, 32, 8));
    225             my $content_off = unpack("Q<", substr($rec, 40, 8));
    226             my $stored_size = unpack("Q<", substr($rec, 48, 8));
    227             my $raw_size = unpack("Q<", substr($rec, 56, 8));
    228             my $compression = unpack("V", substr($rec, 64, 4));
    229             my $reserved = unpack("V", substr($rec, 68, 4));
    230             die "reserved field is nonzero" if $reserved != 0;
    231             die "wrong compression" if $compression != $expected_comp;
    232             die "raw chunk size out of range" if $raw_size == 0 || $raw_size > 65536;
    233             die "content range out of bounds"
    234                 if $content_off > $content_size || $stored_size > $content_size - $content_off;
    235             if ($prev_blob ne "") {
    236                 my $cmp = $prev_blob cmp $blob;
    237                 die "index not sorted" if $cmp > 0;
    238                 die "chunk index not increasing" if $cmp == 0 && $chunk <= $prev_chunk;
    239                 die "new blob does not start at chunk 0" if $cmp < 0 && $chunk != 0;
    240             } elsif ($chunk != 0) {
    241                 die "first blob does not start at chunk 0";
    242             }
    243             $count{$blob}++;
    244             ($prev_blob, $prev_chunk) = ($blob, $chunk);
    245         }
    246         die "unexpected record count" unless $index_size / 168 == 10;
    247         die "empty blob should have no records" if exists $count{$empty};
    248         die "one-byte duplicate blob count" unless ($count{$one} // 0) == 1;
    249         die "chunk64 duplicate blob count" unless ($count{$chunk64} // 0) == 1;
    250         die "chunk64p1 blob count" unless ($count{$chunk64p1} // 0) == 2;
    251         die "chunk3 blob count" unless ($count{$chunk3} // 0) == 4;
    252     ' "$pkg" "$index_off" "$index_size" "$content_size" "$expected_compression" \
    253         "$empty_blob" "$one_blob" "$dup_one_blob" "$chunk64_blob" \
    254         "$chunk64_copy_blob" "$chunk64p1_blob" "$chunk3_blob" \
    255         > "$work/$label.out" 2> "$work/$label.err"; then
    256         ok "$label"
    257     else
    258         not_ok "$label" "$work/$label.err"
    259     fi
    260 }
    261 
    262 maybe_corrupt_native_region() {
    263     label=$1
    264     pkg=$2
    265     descriptor=$3
    266     key=$4
    267     off=$(descriptor_value "$descriptor" "$key")
    268     case "$off" in
    269         ''|*[!0-9]*|0)
    270             skip_test "$label"
    271             ;;
    272         *)
    273             cp "$pkg" "$work/pkg/bad-$label.kpkg"
    274             flip_byte "$work/pkg/bad-$label.kpkg" "$off"
    275             run_fail "$label" "$KIT" pkg verify -p "$work/key.pub" \
    276                 "$work/pkg/bad-$label.kpkg"
    277             ;;
    278     esac
    279 }
    280 
    281 run_matrix_for_package() {
    282     label=$1
    283     pkg=$2
    284     format=$3
    285 
    286     run_ok "$label-inspect" "$KIT" pkg inspect "$pkg"
    287     inspect_checks "$label" "$work/$label-inspect.out"
    288 
    289     run_ok "$label-verify-pubkey" "$KIT" pkg verify -p "$work/key.pub" "$pkg"
    290     contains "$label-verify-output-name" "$work/$label-verify-pubkey.out" "ok: matrix-test 1.0.0"
    291 
    292     outdir="$work/unpack/$label"
    293     mkdir -p "$outdir"
    294     run_ok "$label-unpack-pubkey" "$KIT" pkg unpack --verify -p "$work/key.pub" "$pkg" -C "$outdir"
    295     contains "$label-unpack-verify-output-name" "$work/$label-unpack-pubkey.out" "ok: matrix-test 1.0.0"
    296     same_artifacts "$label-unpack-content" "$outdir"
    297     is_executable "$label-unpack-exec-mode" "$outdir/bin/tool.sh"
    298 
    299     run_ok "$label-verify-format-override" "$KIT" pkg verify -p "$work/key.pub" --format "$format" "$pkg"
    300 }
    301 
    302 make_fixtures
    303 
    304 run_ok "pkg-keygen" "$KIT" pkg keygen -o "$work/key"
    305 keyid=$(keyid_from_keygen "$work/pkg-keygen.out")
    306 if [ -n "$keyid" ]; then
    307     ok "pkg-keygen-keyid"
    308 else
    309     echo "could not parse key id from keygen output" > "$work/pkg-keygen-keyid.diag"
    310     not_ok "pkg-keygen-keyid" "$work/pkg-keygen-keyid.diag"
    311 fi
    312 
    313 printf 'detached verification payload\n' > "$work/detached.txt"
    314 run_ok "pkg-detached-sign" "$KIT" pkg sign -s "$work/key.key" \
    315     --comment "authenticated detached comment" "$work/detached.txt"
    316 run_ok "pkg-detached-verify-explicit-default-sig" "$KIT" pkg verify-signature \
    317     -p "$work/key.pub" "$work/detached.txt"
    318 contains "pkg-detached-trusted-comment" \
    319     "$work/pkg-detached-verify-explicit-default-sig.out" \
    320     "authenticated detached comment"
    321 cp "$work/detached.txt" "$work/detached-changed.txt"
    322 printf tamper >> "$work/detached-changed.txt"
    323 run_fail "pkg-detached-changed-file-fails" "$KIT" pkg verify-signature \
    324     -p "$work/key.pub" -x "$work/detached.txt.minisig" \
    325     "$work/detached-changed.txt"
    326 run_ok "pkg-detached-wrong-keygen" "$KIT" pkg keygen -o "$work/detached-wrong"
    327 run_fail "pkg-detached-wrong-key-fails" "$KIT" pkg verify-signature \
    328     -p "$work/detached-wrong.pub" "$work/detached.txt"
    329 printf 'not a signature\n' > "$work/malformed.minisig"
    330 run_fail "pkg-detached-malformed-fails" "$KIT" pkg verify-signature \
    331     -p "$work/key.pub" -x "$work/malformed.minisig" "$work/detached.txt"
    332 
    333 if have_cmd minisign; then
    334     run_ok "pkg-detached-kit-to-stock" minisign -Vm "$work/detached.txt" \
    335         -p "$work/key.pub" -x "$work/detached.txt.minisig"
    336     run_ok "pkg-detached-stock-sign" minisign -S -W -m "$work/detached.txt" \
    337         -s "$work/key.key" -x "$work/stock.minisig" \
    338         -t "stock authenticated comment"
    339     run_ok "pkg-detached-stock-to-kit" "$KIT" pkg verify-signature \
    340         -p "$work/key.pub" -x "$work/stock.minisig" "$work/detached.txt"
    341     contains "pkg-detached-stock-comment" \
    342         "$work/pkg-detached-stock-to-kit.out" "stock authenticated comment"
    343 else
    344     skip_test "pkg-detached-minisign-interop" "minisign not installed"
    345 fi
    346 
    347 run_ok "pkg-cas-add-tree" "$KIT" cas add-tree --cas "$work/cas" --root "$work/in"
    348 tree_id=$(first_hex_id "$work/pkg-cas-add-tree.out")
    349 if [ -n "$tree_id" ]; then
    350     ok "pkg-cas-tree-id"
    351 else
    352     echo "could not parse tree id from cas add-tree output" > "$work/pkg-cas-tree-id.diag"
    353     not_ok "pkg-cas-tree-id" "$work/pkg-cas-tree-id.diag"
    354 fi
    355 tree_file=$(cas_object_path "$work/cas" tree "$tree_id")
    356 assert_file_exists "pkg-cas-tree-object" "$tree_file"
    357 contains "pkg-cas-tree-magic" "$tree_file" "kit-tree 1"
    358 contains "pkg-cas-tree-exec-mode" "$tree_file" "mode = x"
    359 contains "pkg-cas-tree-file-mode" "$tree_file" "mode = -"
    360 
    361 run_ok "pkg-create-targz-deflate" "$KIT" pkg create \
    362     --name matrix-test --version 1.0.0 --desc "package test matrix" \
    363     --format tar.gz --cas "$work/cas" --tree "$tree_id" \
    364     -s "$work/key.key" -o "$work/pkg/matrix.tar.gz"
    365 
    366 run_ok "pkg-create-kpkg-none" "$KIT" pkg create \
    367     --name matrix-test --version 1.0.0 --desc "package test matrix" \
    368     --format kpkg --compression none --cas "$work/cas" --tree "$tree_id" \
    369     -s "$work/key.key" -o "$work/pkg/matrix-none.kpkg"
    370 
    371 run_ok "pkg-create-kpkg-lz4" "$KIT" pkg create \
    372     --name matrix-test --version 1.0.0 --desc "package test matrix" \
    373     --format kpkg --compression lz4-block-v1 --cas "$work/cas" --tree "$tree_id" \
    374     -s "$work/key.key" -o "$work/pkg/matrix-lz4.kpkg"
    375 
    376 run_ok "pkg-create-root-targz" "$KIT" pkg create \
    377     --name order-test --version 1.0.0 --desc "order independence" \
    378     --format tar.gz --root "$work/in" -s "$work/key.key" \
    379     -o "$work/pkg/order-root.tar.gz"
    380 run_ok "pkg-create-cas-targz" "$KIT" pkg create \
    381     --name order-test --version 1.0.0 --desc "order independence" \
    382     --format tar.gz --cas "$work/cas" --tree "$tree_id" \
    383     -s "$work/key.key" -o "$work/pkg/order-cas.tar.gz"
    384 run_ok "pkg-inspect-order-targz-root" "$KIT" pkg inspect "$work/pkg/order-root.tar.gz"
    385 run_ok "pkg-inspect-order-targz-cas" "$KIT" pkg inspect "$work/pkg/order-cas.tar.gz"
    386 same_file "pkg-root-and-cas-manifest-match" \
    387     "$work/pkg-inspect-order-targz-root.out" \
    388     "$work/pkg-inspect-order-targz-cas.out"
    389 
    390 run_ok "pkg-create-version-alt" "$KIT" pkg create \
    391     --name matrix-test --version 1.0.1 --desc "package test matrix" \
    392     --format tar.gz --cas "$work/cas" --tree "$tree_id" \
    393     -s "$work/key.key" -o "$work/pkg/matrix-alt-version.tar.gz"
    394 run_ok "pkg-inspect-version-alt" "$KIT" pkg inspect "$work/pkg/matrix-alt-version.tar.gz"
    395 contains "pkg-version-alt-tree-same" "$work/pkg-inspect-version-alt.out" "tree = $tree_id"
    396 contains "pkg-version-alt-version" "$work/pkg-inspect-version-alt.out" "version = 1.0.1"
    397 
    398 run_matrix_for_package "targz" "$work/pkg/matrix.tar.gz" "tar.gz"
    399 run_matrix_for_package "kpkg-none" "$work/pkg/matrix-none.kpkg" "kpkg"
    400 run_matrix_for_package "kpkg-lz4" "$work/pkg/matrix-lz4.kpkg" "kpkg"
    401 
    402 if have_cmd perl; then
    403     extract_kpkg3_descriptor "$work/pkg/matrix-none.kpkg" "$work/native-none.descriptor"
    404     contains "native-descriptor-magic" "$work/native-none.descriptor" "kit-encoding 3"
    405     contains "native-descriptor-tree-region" "$work/native-none.descriptor" "tree-offset = "
    406     contains "native-descriptor-index-bytes" "$work/native-none.descriptor" "index-bytes = 1680"
    407     check_kpkg3_index "native-none-index-shape" \
    408         "$work/pkg/matrix-none.kpkg" "$work/native-none.descriptor" 0
    409     extract_kpkg3_descriptor "$work/pkg/matrix-lz4.kpkg" "$work/native-lz4.descriptor"
    410     check_kpkg3_index "native-lz4-index-shape" \
    411         "$work/pkg/matrix-lz4.kpkg" "$work/native-lz4.descriptor" 1
    412     extract_kpkg3_manifest "$work/pkg/matrix-none.kpkg" "$work/native-none.manifest"
    413     contains "native-manifest-v3" "$work/native-none.manifest" "kit-package 3"
    414 else
    415     skip_test "native-descriptor-inspection"
    416 fi
    417 
    418 run_ok "pkg-create-kpkg-thin" "$KIT" pkg create \
    419     --name matrix-test --version 1.0.0 --desc "package test matrix" \
    420     --format kpkg --native-shape thin --external "$work/ext-thin" \
    421     --compression none --cas "$work/cas" --tree "$tree_id" \
    422     -s "$work/key.key" -o "$work/pkg/matrix-thin.kpkg"
    423 run_ok "native-thin-inspect-encoding" "$KIT" pkg inspect --encoding \
    424     "$work/pkg/matrix-thin.kpkg"
    425 contains "native-thin-tree-external" "$work/native-thin-inspect-encoding.out" \
    426     "tree-size = 0"
    427 contains "native-thin-index-external" "$work/native-thin-inspect-encoding.out" \
    428     "index-size = 0"
    429 contains "native-thin-content-external" "$work/native-thin-inspect-encoding.out" \
    430     "content-size = 0"
    431 contains "native-thin-index-url" "$work/native-thin-inspect-encoding.out" \
    432     "index-url = index/"
    433 contains "native-thin-tree-url" "$work/native-thin-inspect-encoding.out" \
    434     "url = tree/"
    435 contains "native-thin-chunk-template" "$work/native-thin-inspect-encoding.out" \
    436     "template = chunk/{blob-prefix}/{blob}/{chunk}"
    437 run_fail "native-thin-verify-without-external-fails" "$KIT" pkg verify \
    438     -p "$work/key.pub" "$work/pkg/matrix-thin.kpkg"
    439 run_ok "native-thin-verify-external" "$KIT" pkg verify \
    440     -p "$work/key.pub" --external "$work/ext-thin" \
    441     "$work/pkg/matrix-thin.kpkg"
    442 mkdir -p "$work/unpack/kpkg-thin"
    443 run_ok "native-thin-unpack-external" "$KIT" pkg unpack --verify \
    444     -p "$work/key.pub" --external "$work/ext-thin" \
    445     "$work/pkg/matrix-thin.kpkg" -C "$work/unpack/kpkg-thin"
    446 same_artifacts "native-thin-unpack-content" "$work/unpack/kpkg-thin"
    447 is_executable "native-thin-unpack-exec-mode" \
    448     "$work/unpack/kpkg-thin/bin/tool.sh"
    449 
    450 run_ok "pkg-create-kpkg-metadata" "$KIT" pkg create \
    451     --name matrix-test --version 1.0.0 --desc "package test matrix" \
    452     --format kpkg --native-shape metadata --external "$work/ext-metadata" \
    453     --compression none --cas "$work/cas" --tree "$tree_id" \
    454     -s "$work/key.key" -o "$work/pkg/matrix-metadata.kpkg"
    455 run_fail "native-metadata-verify-without-external-fails" "$KIT" pkg verify \
    456     -p "$work/key.pub" "$work/pkg/matrix-metadata.kpkg"
    457 run_ok "native-metadata-verify-external" "$KIT" pkg verify \
    458     -p "$work/key.pub" --external "$work/ext-metadata" \
    459     "$work/pkg/matrix-metadata.kpkg"
    460 
    461 payload_blob_for_external=$(tree_blob_for_path "$tree_file" "payload.txt")
    462 payload_blob_prefix=$(id_prefix "$payload_blob_for_external")
    463 cp -R "$work/ext-thin" "$work/ext-thin-bad-chunk"
    464 printf tamper >> "$work/ext-thin-bad-chunk/chunk/$payload_blob_prefix/$payload_blob_for_external/0"
    465 run_fail "native-external-mutated-chunk-fails" "$KIT" pkg verify \
    466     -p "$work/key.pub" --external "$work/ext-thin-bad-chunk" \
    467     "$work/pkg/matrix-thin.kpkg"
    468 
    469 cp -R "$work/ext-thin" "$work/ext-thin-bad-tree"
    470 printf tamper >> "$(cas_object_path "$work/ext-thin-bad-tree" tree "$tree_id")"
    471 run_fail "native-external-mutated-tree-fails" "$KIT" pkg verify \
    472     -p "$work/key.pub" --external "$work/ext-thin-bad-tree" \
    473     "$work/pkg/matrix-thin.kpkg"
    474 
    475 if have_cmd perl; then
    476     extract_kpkg3_descriptor "$work/pkg/matrix-thin.kpkg" "$work/native-thin.descriptor"
    477     thin_index_rel=$(descriptor_value "$work/native-thin.descriptor" "index-url")
    478     cp -R "$work/ext-thin" "$work/ext-thin-bad-index"
    479     printf tamper >> "$work/ext-thin-bad-index/$thin_index_rel"
    480     run_fail "native-external-mutated-index-fails" "$KIT" pkg verify \
    481         -p "$work/key.pub" --external "$work/ext-thin-bad-index" \
    482         "$work/pkg/matrix-thin.kpkg"
    483 else
    484     skip_test "native-external-mutated-index-fails"
    485 fi
    486 
    487 run_ok "host-gzip-accepts-kit-output" gzip -t "$work/pkg/matrix.tar.gz"
    488 if gunzip -c "$work/pkg/matrix.tar.gz" > "$work/pkg/matrix.tar" 2> "$work/gunzip.err" &&
    489    gzip -c "$work/pkg/matrix.tar" > "$work/pkg/host.tar.gz" 2> "$work/regzip.err"; then
    490     run_ok "pkg-inspect-host-gzip" "$KIT" pkg inspect "$work/pkg/host.tar.gz"
    491     run_ok "pkg-verify-host-gzip" "$KIT" pkg verify -p "$work/key.pub" "$work/pkg/host.tar.gz"
    492 else
    493     not_ok "pkg-inspect-host-gzip" "$work/gunzip.err"
    494     not_ok "pkg-verify-host-gzip" "$work/regzip.err"
    495 fi
    496 
    497 run_ok "pkg-create-infer-targz" "$KIT" pkg create \
    498     --name matrix-test --version 1.0.0 --root "$work/in" -s "$work/key.key" \
    499     -o "$work/pkg/infer.tar.gz"
    500 run_ok "pkg-create-infer-kpkg" "$KIT" pkg create \
    501     --name matrix-test --version 1.0.0 --root "$work/in" -s "$work/key.key" \
    502     -o "$work/pkg/infer.kpkg"
    503 run_fail "pkg-create-unknown-extension-needs-format" "$KIT" pkg create \
    504     --name matrix-test --version 1.0.0 -s "$work/key.key" \
    505     --root "$work/in" -o "$work/pkg/unknown.pkg"
    506 run_ok "pkg-create-override-targz-extension" "$KIT" pkg create \
    507     --name matrix-test --version 1.0.0 --format tar.gz -s "$work/key.key" \
    508     --root "$work/in" -o "$work/pkg/override.pkg"
    509 run_ok "pkg-verify-override-targz-extension" "$KIT" pkg verify -p "$work/key.pub" \
    510     --format tar.gz "$work/pkg/override.pkg"
    511 run_fail "pkg-verify-wrong-format-native" "$KIT" pkg verify -p "$work/key.pub" \
    512     --format kpkg "$work/pkg/matrix.tar.gz"
    513 run_fail "pkg-verify-wrong-format-portable" "$KIT" pkg verify -p "$work/key.pub" \
    514     --format tar.gz "$work/pkg/matrix-none.kpkg"
    515 
    516 run_fail "pkg-verify-untrusted-no-key" "$KIT" pkg verify "$work/pkg/matrix.tar.gz"
    517 run_ok "pkg-trust-path" "$KIT" pkg trust path
    518 contains "pkg-trust-path-output" "$work/pkg-trust-path.out" "$KIT_TRUSTED_KEYS"
    519 run_ok "pkg-trust-list-empty" "$KIT" pkg trust list
    520 contains "pkg-trust-list-empty-message" "$work/pkg-trust-list-empty.out" "(no trusted keys"
    521 run_ok "pkg-trust-add" "$KIT" pkg trust add "$work/key.pub" matrix-label
    522 run_ok "pkg-trust-list-added" "$KIT" pkg trust list
    523 contains "pkg-trust-list-added-key" "$work/pkg-trust-list-added.out" "$keyid"
    524 contains "pkg-trust-list-added-label" "$work/pkg-trust-list-added.out" "matrix-label"
    525 run_ok "pkg-verify-trusted-store" "$KIT" pkg verify "$work/pkg/matrix.tar.gz"
    526 run_ok "pkg-detached-verify-trusted-store" "$KIT" pkg verify-signature \
    527     "$work/detached.txt"
    528 run_ok "pkg-trust-remove" "$KIT" pkg trust remove "$keyid"
    529 run_ok "pkg-trust-list-removed" "$KIT" pkg trust list
    530 not_contains "pkg-trust-list-removed-key" "$work/pkg-trust-list-removed.out" "$keyid"
    531 run_fail "pkg-verify-after-trust-remove" "$KIT" pkg verify "$work/pkg/matrix.tar.gz"
    532 
    533 mkdir -p "$work/tofu-home"
    534 run_ok "pkg-verify-tofu-pins" env HOME="$work/tofu-home" KIT_TRUSTED_KEYS="$work/tofu.keys" \
    535     "$KIT" pkg verify --tofu "$work/pkg/matrix.tar.gz"
    536 contains "pkg-tofu-file-key" "$work/tofu.keys" "$keyid"
    537 run_ok "pkg-verify-tofu-store" env HOME="$work/tofu-home" KIT_TRUSTED_KEYS="$work/tofu.keys" \
    538     "$KIT" pkg verify "$work/pkg/matrix.tar.gz"
    539 
    540 run_ok "pkg-keygen-wrong" "$KIT" pkg keygen -o "$work/wrong"
    541 run_fail "pkg-verify-wrong-pubkey" "$KIT" pkg verify -p "$work/wrong.pub" "$work/pkg/matrix.tar.gz"
    542 
    543 if have_cmd tar; then
    544     extract_targz "$work/pkg/matrix.tar.gz" "$work/tar-ok"
    545     tar_tree_file=$(cas_object_path "$work/tar-ok/kit/cas" tree "$tree_id")
    546     for f in kit/package.manifest kit/package.manifest.minisig kit/package.pub; do
    547         safe_f=$(printf '%s\n' "$f" | sed 's#[^A-Za-z0-9_.-]#-#g')
    548         if [ -f "$work/tar-ok/$f" ]; then
    549             ok "portable-member-$f"
    550         else
    551             echo "missing tar member: $f" > "$work/portable-member-$safe_f.diag"
    552             not_ok "portable-member-$f" "$work/portable-member-$safe_f.diag"
    553         fi
    554     done
    555     assert_file_exists "portable-member-tree-object" "$tar_tree_file"
    556     same_file "portable-tree-matches-cas" "$tree_file" "$tar_tree_file"
    557     for f in $artifacts; do
    558         blob=$(tree_blob_for_path "$tar_tree_file" "$f")
    559         blob_file=$(cas_object_path "$work/tar-ok/kit/cas" blob "$blob")
    560         safe_f=$(printf '%s\n' "$f" | sed 's#[^A-Za-z0-9_.-]#-#g')
    561         assert_file_exists "portable-member-blob-$safe_f" "$blob_file"
    562     done
    563     if [ -f "$work/tar-ok/payload.txt" ] || [ -f "$work/tar-ok/bin/tool.sh" ]; then
    564         echo "portable archive stored output files outside kit/cas" > "$work/portable-no-root-files.diag"
    565         not_ok "portable-no-root-files" "$work/portable-no-root-files.diag"
    566     else
    567         ok "portable-no-root-files"
    568     fi
    569 
    570     extract_targz "$work/pkg/matrix-alt-version.tar.gz" "$work/tar-alt-version"
    571     contains "portable-alt-manifest-tree-same" \
    572         "$work/tar-alt-version/kit/package.manifest" "tree = $tree_id"
    573     not_same_file "portable-metadata-changes-manifest" \
    574         "$work/tar-ok/kit/package.manifest" \
    575         "$work/tar-alt-version/kit/package.manifest"
    576     same_file "portable-metadata-keeps-tree-object" \
    577         "$tar_tree_file" \
    578         "$(cas_object_path "$work/tar-alt-version/kit/cas" tree "$tree_id")"
    579 
    580     cp -R "$work/tar-ok" "$work/tar-missing-manifest"
    581     rm -f "$work/tar-missing-manifest/kit/package.manifest"
    582     repack_targz "$work/tar-missing-manifest" "$work/pkg/bad-missing-manifest.tar.gz"
    583     run_fail "portable-missing-manifest-fails" "$KIT" pkg verify -p "$work/key.pub" \
    584         "$work/pkg/bad-missing-manifest.tar.gz"
    585 
    586     cp -R "$work/tar-ok" "$work/tar-missing-signature"
    587     rm -f "$work/tar-missing-signature/kit/package.manifest.minisig"
    588     repack_targz "$work/tar-missing-signature" "$work/pkg/bad-missing-signature.tar.gz"
    589     run_fail "portable-missing-signature-fails" "$KIT" pkg verify -p "$work/key.pub" \
    590         "$work/pkg/bad-missing-signature.tar.gz"
    591 
    592     cp -R "$work/tar-ok" "$work/tar-missing-tree"
    593     rm -f "$(cas_object_path "$work/tar-missing-tree/kit/cas" tree "$tree_id")"
    594     repack_targz "$work/tar-missing-tree" "$work/pkg/bad-missing-tree.tar.gz"
    595     run_fail "portable-missing-tree-fails" "$KIT" pkg verify -p "$work/key.pub" \
    596         "$work/pkg/bad-missing-tree.tar.gz"
    597 
    598     payload_blob=$(tree_blob_for_path "$tar_tree_file" "payload.txt")
    599     cp -R "$work/tar-ok" "$work/tar-missing-blob"
    600     rm -f "$(cas_object_path "$work/tar-missing-blob/kit/cas" blob "$payload_blob")"
    601     repack_targz "$work/tar-missing-blob" "$work/pkg/bad-missing-blob.tar.gz"
    602     run_fail "portable-missing-blob-fails" "$KIT" pkg verify -p "$work/key.pub" \
    603         "$work/pkg/bad-missing-blob.tar.gz"
    604 
    605     cp -R "$work/tar-ok" "$work/tar-mutated-blob"
    606     printf tamper >> "$(cas_object_path "$work/tar-mutated-blob/kit/cas" blob "$payload_blob")"
    607     repack_targz "$work/tar-mutated-blob" "$work/pkg/bad-mutated-blob.tar.gz"
    608     run_fail "portable-mutated-blob-fails" "$KIT" pkg verify -p "$work/key.pub" \
    609         "$work/pkg/bad-mutated-blob.tar.gz"
    610 
    611     cp -R "$work/tar-ok" "$work/tar-mutated-tree"
    612     printf '\n# tamper\n' >> "$(cas_object_path "$work/tar-mutated-tree/kit/cas" tree "$tree_id")"
    613     repack_targz "$work/tar-mutated-tree" "$work/pkg/bad-mutated-tree.tar.gz"
    614     run_fail "portable-mutated-tree-fails" "$KIT" pkg verify -p "$work/key.pub" \
    615         "$work/pkg/bad-mutated-tree.tar.gz"
    616 
    617     cp -R "$work/tar-ok" "$work/tar-mutated-manifest"
    618     printf '\n# tamper\n' >> "$work/tar-mutated-manifest/kit/package.manifest"
    619     repack_targz "$work/tar-mutated-manifest" "$work/pkg/bad-mutated-manifest.tar.gz"
    620     run_fail "portable-mutated-manifest-fails" "$KIT" pkg verify -p "$work/key.pub" \
    621         "$work/pkg/bad-mutated-manifest.tar.gz"
    622 else
    623     skip_test "portable-tar-member-and-repack-tests"
    624 fi
    625 
    626 cp "$work/pkg/matrix.tar.gz" "$work/pkg/bad-gzip-byte.tar.gz"
    627 flip_byte "$work/pkg/bad-gzip-byte.tar.gz" 0
    628 run_fail "portable-corrupt-gzip-fails" "$KIT" pkg verify -p "$work/key.pub" \
    629     "$work/pkg/bad-gzip-byte.tar.gz"
    630 
    631 cp "$work/pkg/matrix-none.kpkg" "$work/pkg/bad-native-magic.kpkg"
    632 flip_byte "$work/pkg/bad-native-magic.kpkg" 0
    633 run_fail "native-bad-magic-fails" "$KIT" pkg verify -p "$work/key.pub" \
    634     "$work/pkg/bad-native-magic.kpkg"
    635 
    636 dd if="$work/pkg/matrix-none.kpkg" of="$work/pkg/bad-native-truncated.kpkg" \
    637     bs=1 count=100 >/dev/null 2>&1
    638 run_fail "native-truncated-fails" "$KIT" pkg verify -p "$work/key.pub" \
    639     "$work/pkg/bad-native-truncated.kpkg"
    640 
    641 cp "$work/pkg/matrix-none.kpkg" "$work/pkg/bad-native-manifest.kpkg"
    642 if have_cmd perl; then
    643     manifest_off=$(kpkg3_field_offset "$work/pkg/matrix-none.kpkg" 0)
    644     flip_byte "$work/pkg/bad-native-manifest.kpkg" "$manifest_off"
    645     run_fail "native-mutated-manifest-fails" "$KIT" pkg verify -p "$work/key.pub" \
    646         "$work/pkg/bad-native-manifest.kpkg"
    647 else
    648     skip_test "native-mutated-manifest-fails"
    649 fi
    650 
    651 if have_cmd perl; then
    652     descriptor_off=$(kpkg3_field_offset "$work/pkg/matrix-none.kpkg" 4)
    653     extract_kpkg3_descriptor "$work/pkg/matrix-none.kpkg" "$work/native-corrupt.descriptor"
    654 
    655     cp "$work/pkg/matrix-none.kpkg" "$work/pkg/bad-native-descriptor.kpkg"
    656     flip_byte "$work/pkg/bad-native-descriptor.kpkg" "$descriptor_off"
    657     run_fail "native-mutated-descriptor-fails" "$KIT" pkg verify -p "$work/key.pub" \
    658         "$work/pkg/bad-native-descriptor.kpkg"
    659 
    660     maybe_corrupt_native_region "native-mutated-tree-region-fails" \
    661         "$work/pkg/matrix-none.kpkg" "$work/native-corrupt.descriptor" "tree-offset"
    662     maybe_corrupt_native_region "native-mutated-index-fails" \
    663         "$work/pkg/matrix-none.kpkg" "$work/native-corrupt.descriptor" "index-offset"
    664     maybe_corrupt_native_region "native-mutated-content-fails" \
    665         "$work/pkg/matrix-none.kpkg" "$work/native-corrupt.descriptor" "content-offset"
    666 else
    667     skip_test "native-region-offset-corruption-tests"
    668 fi
    669 
    670 run_fail "pkg-create-missing-required-fails" "$KIT" pkg create \
    671     --name missing-version -s "$work/key.key" --root "$work/in" -o "$work/pkg/missing.tar.gz"
    672 run_fail "pkg-create-unknown-format-fails" "$KIT" pkg create \
    673     --name matrix-test --version 1.0.0 --format nope -s "$work/key.key" \
    674     --root "$work/in" -o "$work/pkg/badformat.tar.gz"
    675 run_fail "pkg-create-unknown-compression-fails" "$KIT" pkg create \
    676     --name matrix-test --version 1.0.0 --format kpkg --compression nope \
    677     -s "$work/key.key" --root "$work/in" -o "$work/pkg/badcomp.kpkg"
    678 mkdir -p "$work/dup/a" "$work/dup/b"
    679 printf one > "$work/dup/a/same.dat"
    680 printf two > "$work/dup/b/same.dat"
    681 cat > "$work/dup.map" <<EOF
    682 same.dat - $work/dup/a/same.dat
    683 same.dat - $work/dup/b/same.dat
    684 EOF
    685 run_fail "pkg-cas-duplicate-tree-path-fails" "$KIT" cas add-tree \
    686     --cas "$work/cas" --map "$work/dup.map"
    687 printf unsafe > "$work/in/bad\\path.dat"
    688 cat > "$work/unsafe.map" <<EOF
    689 bad\\path.dat - $work/in/bad\\path.dat
    690 EOF
    691 run_fail "pkg-cas-unsafe-tree-path-fails" "$KIT" cas add-tree \
    692     --cas "$work/cas" --map "$work/unsafe.map"
    693 run_fail "pkg-verify-missing-file-fails" "$KIT" pkg verify -p "$work/key.pub" \
    694     "$work/pkg/does-not-exist.tar.gz"
    695 printf not-a-package > "$work/not-a-package.kpkg"
    696 run_fail "pkg-inspect-malformed-fails" "$KIT" pkg inspect "$work/not-a-package.kpkg"
    697 run_fail "pkg-trust-add-missing-pubkey-fails" "$KIT" pkg trust add
    698 run_fail "pkg-trust-remove-bad-keyid-fails" "$KIT" pkg trust remove xyz
    699 
    700 kit_summary pkg
    701 kit_exit