unos-repository

APK repository for unos
git clone git://git.finwo.net/misc/unos-repository
Log | Files | Refs | README

keymgmt.sh (4825B)


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