keymgmt.sh (4708B)
1 #!/bin/sh 2 # mk/keymgmt.sh - signing key management. 3 # 4 # keymgmt.sh new [--type rsa|supercop] [--for apk|unos] <stem> 5 # Generate a key named <stem>-<8 hex> in ~/.unos-keys/ and stage its 6 # public half into packages/unos-keys/files/<for>/. Defaults: rsa for 7 # apk (4096-bit), supercop for unos. apk mandates RSA: supercop-for-apk 8 # is refused. After adding keys, bump the unos-keys version and rebuild. 9 # 10 # keymgmt.sh use <name|file> 11 # Select the RSA key used to sign packages; writes its full path to 12 # .sign-key (repo root, never committed). CI sets UNOS_SIGN_KEY or 13 # writes .sign-key itself. Accepts a stem, a filename in ~/.unos-keys/, 14 # or a path. 15 # 16 # keymgmt.sh list 17 # Show local keys and which one is active. 18 set -eu 19 20 HERE=$(cd "$(dirname "$0")" && pwd) 21 ROOT=$(cd "${HERE}/.." && pwd) 22 KEYDIR="${HOME}/.unos-keys" 23 PKGKEYS="${ROOT}/packages/unos-keys/files" 24 25 msg() { printf '==> %s\n' "$*"; } 26 die() { printf 'keymgmt.sh: error: %s\n' "$*" >&2; exit 1; } 27 28 valid_stem() { 29 case "$1" in 30 ''|*[!A-Za-z0-9@._-]*) return 1;; 31 *) return 0;; 32 esac 33 } 34 35 cmd_new() { 36 type=rsa 37 for= 38 while [ $# -gt 0 ]; do 39 case "$1" in 40 --type) type=$2; shift 2;; 41 --for) for=$2; shift 2;; 42 -h|--help) cmd_usage; exit 0;; 43 --*) die "unknown flag: $1";; 44 *) break;; 45 esac 46 done 47 [ $# = 1 ] || die "usage: keymgmt.sh new [--type rsa|supercop] [--for apk|unos] <stem>" 48 stem=$1 49 valid_stem "${stem}" || die "bad stem (allowed: A-Z a-z 0-9 @ . _ -): ${stem}" 50 case "${type}" in rsa|supercop) ;; *) die "type must be rsa or supercop";; esac 51 if [ -z "${for}" ]; then 52 if [ "${type}" = rsa ]; then for=apk; else for=unos; fi 53 fi 54 case "${for}" in apk|unos) ;; *) die "--for must be apk or unos";; esac 55 if [ "${for}" = apk ] && [ "${type}" != rsa ]; then 56 die "apk signatures mandate RSA; supercop keys are unos-only" 57 fi 58 59 mkdir -p "${KEYDIR}" 60 chmod 700 "${KEYDIR}" 61 i=0 62 while :; do 63 suffix=$(openssl rand -hex 4) 64 base="${stem}-${suffix}" 65 case "${type}" in 66 rsa) priv="${base}.rsa"; pub="${base}.rsa.pub";; 67 supercop) priv="${base}.ed25519"; pub="${base}.ed25519.pub";; 68 esac 69 [ -e "${KEYDIR}/${priv}" ] || [ -e "${KEYDIR}/${pub}" ] || break 70 i=$((i + 1)) 71 [ "${i}" -lt 10 ] || die "cannot find a free key name, retry" 72 done 73 74 case "${type}" in 75 rsa) 76 openssl genrsa -out "${KEYDIR}/${priv}" 4096 2>/dev/null 77 openssl rsa -in "${KEYDIR}/${priv}" -pubout -out "${KEYDIR}/${pub}" 2>/dev/null 78 # Enforce minimum (defence in depth, even though we just generated 4096) 79 "${HERE}/check-keysize.sh" "${KEYDIR}/${priv}" 4096 >/dev/null 80 ;; 81 supercop) 82 supercop generate -k "${KEYDIR}/${priv}" 83 supercop printkey -k "${KEYDIR}/${priv}" --public-only -f asc -o "${KEYDIR}/${pub}" 84 ;; 85 esac 86 chmod 600 "${KEYDIR}/${priv}" 87 88 mkdir -p "${PKGKEYS}/${for}" 89 cp "${KEYDIR}/${pub}" "${PKGKEYS}/${for}/${pub}" 90 msg "key: ${KEYDIR}/${priv}" 91 msg "pub staged: packages/unos-keys/files/${for}/${pub}" 92 echo "next: bump packages/unos-keys version, rebuild it, re-index" 93 } 94 95 cmd_use() { 96 [ $# = 1 ] || die "usage: keymgmt.sh use <name|file>" 97 arg=$1 98 if [ -f "${arg}" ]; then 99 case "${arg}" in 100 /*) path=${arg};; 101 *) path=${PWD}/${arg};; 102 esac 103 elif [ -f "${KEYDIR}/${arg}" ]; then 104 path=${KEYDIR}/${arg} 105 elif [ -f "${KEYDIR}/${arg}.rsa" ]; then 106 path=${KEYDIR}/${arg}.rsa 107 else 108 die "no such key: ${arg}" 109 fi 110 openssl rsa -in "${path}" -check -noout >/dev/null 2>&1 \ 111 || die "not an RSA private key: ${path}" 112 "${HERE}/check-keysize.sh" "${path}" 4096 >/dev/null \ 113 || die "key too small (4096-bit minimum): ${path}" 114 printf '%s\n' "${path}" > "${ROOT}/.sign-key" 115 msg "signing key: ${path}" 116 } 117 118 cmd_list() { 119 # shellcheck disable=SC1090 120 . "${HERE}/sign-key.inc" 121 active=$(resolve_sign_key 2>/dev/null) || active= 122 found=0 123 for f in "${KEYDIR}"/*; do 124 [ -e "${f}" ] || continue 125 found=1 126 mark=" " 127 [ "${f}" = "${active}" ] && mark="*" 128 case "${f}" in 129 *.rsa) kind="rsa";; 130 *.rsa.pub) kind="rsa-pub";; 131 *.ed25519) kind="ed25519";; 132 *.ed25519.pub) kind="ed25519-pub";; 133 *) kind="?";; 134 esac 135 printf '%s %-12s %s\n' "${mark}" "${kind}" "${f}" 136 done 137 [ "${found}" = 1 ] || echo "(no keys in ${KEYDIR})" 138 } 139 140 cmd_usage() { 141 cat <<EOF 142 usage: keymgmt.sh new [--type rsa|supercop] [--for apk|unos] <stem> 143 keymgmt.sh use <name|file> 144 keymgmt.sh list 145 EOF 146 } 147 148 [ $# -ge 1 ] || { cmd_usage >&2; exit 1; } 149 cmd=$1; shift 150 case "${cmd}" in 151 new) cmd_new "$@";; 152 use) cmd_use "$@";; 153 list) cmd_list "$@";; 154 -h|--help) cmd_usage; exit 0;; 155 *) cmd_usage >&2; exit 1;; 156 esac