kit

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

commit 3227411c46ee24313aa87c0dfd50467188faba13
parent 771e83607222a3648f4d522a1c35ad88d4817595
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 16 Jul 2026 11:17:42 -0700

dist: reject reserialized nonrelease signer

Diffstat:
Mmk/dist.mk | 6+++---
Mmk/flags.mk | 4++--
Ascripts/minisign_key_id.sh | 46++++++++++++++++++++++++++++++++++++++++++++++
Dscripts/minisign_key_material.sh | 22----------------------
Mscripts/release.sh | 12++++++------
Mtest/dist/run.sh | 14++++++++++++++
6 files changed, 71 insertions(+), 33 deletions(-)

diff --git a/mk/dist.mk b/mk/dist.mk @@ -18,9 +18,9 @@ ifeq ($(strip $(KIT_SIGN_KEY)),) $(error dist requires KIT_SIGN_KEY (production Minisign secret key file)) endif ifneq ($(KIT_RELEASE_ALLOW_TEST_KEY),1) -DIST_TEST_KEY_MATERIAL := $(strip $(shell sh scripts/minisign_key_material.sh test/dist/keys/nonrelease.key 2>/dev/null)) -DIST_SIGN_KEY_MATERIAL := $(strip $(shell sh scripts/minisign_key_material.sh "$(KIT_SIGN_KEY)" 2>/dev/null)) -ifeq ($(DIST_SIGN_KEY_MATERIAL),$(DIST_TEST_KEY_MATERIAL)) +DIST_TEST_KEY_ID := $(strip $(shell sh scripts/minisign_key_id.sh test/dist/keys/nonrelease.key 2>/dev/null)) +DIST_SIGN_KEY_ID := $(strip $(shell sh scripts/minisign_key_id.sh "$(KIT_SIGN_KEY)" 2>/dev/null)) +ifeq ($(DIST_SIGN_KEY_ID),$(DIST_TEST_KEY_ID)) $(error dist refuses the repository NON-RELEASE test signing key) endif endif diff --git a/mk/flags.mk b/mk/flags.mk @@ -164,8 +164,8 @@ ifeq ($(strip $(KIT_RELEASE_PUBKEYS)),) $(error RELEASE=1 requires KIT_RELEASE_PUBKEYS (production Minisign public key file(s))) endif ifneq ($(KIT_RELEASE_ALLOW_TEST_KEY),1) -KIT_RELEASE_TEST_KEY_MATERIAL := $(strip $(shell sh scripts/minisign_key_material.sh test/dist/keys/nonrelease.pub 2>/dev/null)) -KIT_RELEASE_TEST_ANCHOR := $(strip $(foreach key,$(KIT_RELEASE_PUBKEYS),$(if $(filter $(KIT_RELEASE_TEST_KEY_MATERIAL),$(strip $(shell sh scripts/minisign_key_material.sh "$(key)" 2>/dev/null))),$(key)))) +KIT_RELEASE_TEST_KEY_ID := $(strip $(shell sh scripts/minisign_key_id.sh test/dist/keys/nonrelease.pub 2>/dev/null)) +KIT_RELEASE_TEST_ANCHOR := $(strip $(foreach key,$(KIT_RELEASE_PUBKEYS),$(if $(filter $(KIT_RELEASE_TEST_KEY_ID),$(strip $(shell sh scripts/minisign_key_id.sh "$(key)" 2>/dev/null))),$(key)))) ifneq ($(KIT_RELEASE_TEST_ANCHOR),) $(error RELEASE=1 refuses repository NON-RELEASE test anchor: $(KIT_RELEASE_TEST_ANCHOR)) endif diff --git a/scripts/minisign_key_id.sh b/scripts/minisign_key_id.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# Decode the stable 8-byte signer identity from a Minisign public or secret +# key. Pure POSIX awk keeps release gating independent of host base64 variants. + +set -eu + +[ "$#" -eq 1 ] || { + printf 'usage: minisign_key_id.sh KEY\n' >&2 + exit 2 +} + +LC_ALL=C awk ' + function b64(c) { + if (c == "=") return -1 + return index("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", c) - 1 + } + /^untrusted comment:/ { next } + /^[[:space:]]*$/ { next } + { + s = $0 + gsub(/[[:space:]]/, "", s) + n = 0 + for (i = 1; i <= length(s); i += 4) { + a = b64(substr(s, i, 1)) + b = b64(substr(s, i + 1, 1)) + c = b64(substr(s, i + 2, 1)) + d = b64(substr(s, i + 3, 1)) + if (a < 0 || b < 0) exit 1 + byte[++n] = a * 4 + int(b / 16) + if (c >= 0) byte[++n] = (b % 16) * 16 + int(c / 4) + if (d >= 0) byte[++n] = (c % 4) * 64 + d + } + if (byte[1] != 69 || byte[2] != 100) exit 1 + if (n == 42) + off = 3 + else if (n == 158) + off = 55 + else + exit 1 + found = 1 + for (i = off; i < off + 8; ++i) printf "%02x", byte[i] + printf "\n" + exit + } + END { if (!found) exit 1 } +' "$1" diff --git a/scripts/minisign_key_material.sh b/scripts/minisign_key_material.sh @@ -1,22 +0,0 @@ -#!/bin/sh -# Print the first Minisign key-material record, ignoring the mutable untrusted -# comment. Works for both public and passwordless secret-key files. - -set -eu - -[ "$#" -eq 1 ] || { - printf 'usage: minisign_key_material.sh KEY\n' >&2 - exit 2 -} - -LC_ALL=C awk ' - /^untrusted comment:/ { next } - /^[[:space:]]*$/ { next } - { - gsub(/[[:space:]]/, "") - print - found = 1 - exit - } - END { if (!found) exit 1 } -' "$1" diff --git a/scripts/release.sh b/scripts/release.sh @@ -87,15 +87,15 @@ done "KIT_UPDATE_INDEX_URL is unset — provide the production stable channel URL" TEST_SIGN_KEY="$ROOT/test/dist/keys/nonrelease.key" TEST_PUBKEY="$ROOT/test/dist/keys/nonrelease.pub" -TEST_SIGN_MATERIAL="$(sh "$ROOT/scripts/minisign_key_material.sh" "$TEST_SIGN_KEY")" -TEST_PUB_MATERIAL="$(sh "$ROOT/scripts/minisign_key_material.sh" "$TEST_PUBKEY")" +TEST_SIGN_ID="$(sh "$ROOT/scripts/minisign_key_id.sh" "$TEST_SIGN_KEY")" +TEST_PUB_ID="$(sh "$ROOT/scripts/minisign_key_id.sh" "$TEST_PUBKEY")" if [ "${KIT_RELEASE_ALLOW_TEST_KEY:-0}" != 1 ]; then - [ "$(sh "$ROOT/scripts/minisign_key_material.sh" "$KIT_SIGN_KEY")" != \ - "$TEST_SIGN_MATERIAL" ] || + [ "$(sh "$ROOT/scripts/minisign_key_id.sh" "$KIT_SIGN_KEY")" != \ + "$TEST_SIGN_ID" ] || die "KIT_SIGN_KEY contains the repository NON-RELEASE test key; set KIT_SIGN_KEY to the real release key" for release_pubkey in $KIT_RELEASE_PUBKEYS; do - [ "$(sh "$ROOT/scripts/minisign_key_material.sh" "$release_pubkey")" != \ - "$TEST_PUB_MATERIAL" ] || + [ "$(sh "$ROOT/scripts/minisign_key_id.sh" "$release_pubkey")" != \ + "$TEST_PUB_ID" ] || die "KIT_RELEASE_PUBKEYS contains the repository NON-RELEASE test anchor: $release_pubkey" done fi diff --git a/test/dist/run.sh b/test/dist/run.sh @@ -76,6 +76,20 @@ run_fail "release-dist-copied-test-key-rejected" make -n -C "$repo_root" dist \ KIT_SIGN_KEY="$work/copied-nonrelease.key" \ KIT_UPDATE_INDEX_URL=https://updates.example/stable.index \ KIT_RELEASE_PUBKEYS="$work/release-config.pub" +# Passwordless Minisign ignores its salt field. Mutating that field produces a +# different base64 record for the same valid signer identity; the release gate +# must still recognize it rather than relying on raw record equality. +{ + printf '%s\n' 'untrusted comment: reserialized release-looking secret key' + sed -n '2p' "$SECKEY" | awk '{ print substr($0, 1, 12) "B" substr($0, 14) }' +} > "$work/reserialized-nonrelease.key" +printf 'identity regression payload\n' > "$work/identity-payload" +run_ok "release-reserialized-test-key-still-signs" "$KIT" pkg sign \ + -s "$work/reserialized-nonrelease.key" "$work/identity-payload" +run_fail "release-dist-reserialized-test-key-rejected" make -n \ + -C "$repo_root" dist KIT_SIGN_KEY="$work/reserialized-nonrelease.key" \ + KIT_UPDATE_INDEX_URL=https://updates.example/stable.index \ + KIT_RELEASE_PUBKEYS="$work/release-config.pub" run_fail "release-script-copied-test-key-rejected" env \ KIT_SIGN_KEY="$work/copied-nonrelease.key" \ KIT_UPDATE_INDEX_URL=https://updates.example/stable.index \