diff --git a/.sig/manifest.8E47A1EC35A1551D.asc b/.sig/manifest.8E47A1EC35A1551D.asc index 92a855e..c9e389e 100644 --- a/.sig/manifest.8E47A1EC35A1551D.asc +++ b/.sig/manifest.8E47A1EC35A1551D.asc @@ -1,16 +1,16 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCgAdFiEEZ1U/vaRrtxq9LgsLjkeh7DWhVR0FAl+zF58ACgkQjkeh7DWh -VR2DbQ//fLBe5OUcrT7O4TWguYPOYmQJSNM7NWG2tNS4roiJFaJUPhx0djnEIhqI -4Ui8FJfXyowHYDyxbtzL165bl2LcjUVlp2+xyTZxZwqh/TGVSqSy0tioepiWiTO3 -f/aVki7j7TkGwi/fPb1t7WpwbGlMxgXDGFsYYZ8+MWi7XQEg72bl+dOHUy8EsDBd -jZKfWuc6NcpUwip814WAQBTWQbRrNdyTPTn/jJ974Ba75trEY9RMbMbaDw80Aw98 -KoRSo6zDu/xT9UBoxWfRO+aMahtYqKNO+F8PmXFxXGI0Gi3iwciv82H5h3KH52Cx -LHAeUApApI5xD0A7s/UfOei48ed34XiSymbJG6ppIwaEsXNIgbe3qhdx4YvGQ9X0 -euvRGxRGbOZjCPalqQfu4Z7CWywTPdX2q/usyPe7u+D8SzD0ICdhBk0A86qi4J84 -/1cWHmtotssN9tNNLGGoGKmDkxawqElQWeHTlNvxzFzW5CI9KyYPD5cxaFtr+jsY -yc2Av6PDnKOLH3HFUo03eoeq9p7ov5NGQswDFxdlTEDcbpaPZDcYiV+Z5zJPMJOs -ve2otw9WcximzSs9mryNgLEFSlRTG31oNzLI5E92oHS3pkYRNgW0Wr1SpzetRk+6 -JhCp7f3C2dwUx3/TxeNX/F5j/vQvYrbS0Km83a7CzgEIvfrJEYs= -=8zXb +iQIzBAABCgAdFiEEZ1U/vaRrtxq9LgsLjkeh7DWhVR0FAl+0XVoACgkQjkeh7DWh +VR3+gxAAnPUlZ7zVrk0DuTGpozySfgh2GL0oekFqtQqy5XwC7A5Etq4VznGmpkOJ +PIvFjv1GYdpVFFlLEmf/rgLg2e15K0gkxtOKqGZ8w9zih87gA71N1GXe9SpvkHtu +n5mPziYHZVU/ESQoMbGAO+/Hn8ctP/gSITo+br7IqwOOuxD1H/MCYOFqqO4ZGpF6 +JSacNuGD4hdEhLZXjSWM+mx67J3WbbIR6NHJW/iq4iTNq+Snc/OGl3hjCZEk935o +9LjuKEdaqgpaEDUuQKBtLl2HOYgSR0NlV0OkrTAfNPZhFYHwcoxH12JBHmSU+MnI +0vMmL9TL1JyTSiC2nNuhbd07t9bTV1WeTZe9Iu8qGpCuOrSiKnptef/cRUDseZhc +PyoYRW8rY8u54mvQJmepTMNy6pRY5OlTvGysWXTmmQE/TyTGHzvz0llFKcO6nJdz +SbuCi9lF8tz/Po5XwF6Be3YS6wO0cS53A+tAq05nUryxGAI4TaS6OTvE9Fj9medp +oWUD49xb4dB2sNEye0Lg83HKjqBbEAIUA5wOejYeLFxfLFuNYT1Pjt8PAwPJmnKA +Bzc1H7M36pcpgj2i/FvLmtGn3BpkvtmShO904BNNCpV8ymwKFYBAbZmlpNwCg2ST ++Px+Eplb2EijzBsGZSAikOaVjoxbEsucI7VFRvmPzJrmOSZRpWs= +=8C5n -----END PGP SIGNATURE----- diff --git a/.sig/manifest.txt b/.sig/manifest.txt index 9c9914c..6ea3282 100644 --- a/.sig/manifest.txt +++ b/.sig/manifest.txt @@ -1,3 +1,3 @@ 64263feac7b00952e9ec3b6c1fd11316faa58ff673c6bd085fac9f6f8d8389f6 .gitignore c5649b242a88aec7617c627b75d3d6c6057123fdd9f1ebd8fee1d7001a827da0 README.md -bbe6f757d0f17cd0eab5b4cafbd7351e2cd403a7386ac47a2a894115b6f29683 sig +e02b0ef74b361497249967dd13477c988c602a187d76d610c05f9193eaec6cb4 sig diff --git a/sig b/sig index 1a68565..1714d84 100755 --- a/sig +++ b/sig @@ -40,6 +40,32 @@ die_pkg() { exit 1 } +### Ask user to make a binary decision +ask() { + local prompt default + while true; do + prompt="" + default="" + if [ "${2}" = "Y" ]; then + prompt="Y/n" + default=Y + elif [ "${2}" = "N" ]; then + prompt="y/N" + default=N + else + prompt="y/n" + default= + fi + printf "\\n%s [%s] " "$1" "$prompt" + read -r reply + [ -z "$reply" ] && reply=$default + case "$reply" in + Y*|y*) return 0 ;; + N*|n*) return 1 ;; + esac + done +} + ### Check if actual binary version is >= minimum version check_version(){ local pkg="${1?}" @@ -96,23 +122,100 @@ get_files(){ fi } -### Get signer name/email from key fingerprint -get_signer(){ - local fingerprint="${1?}" - gpg \ - --list-keys \ - --with-colons "${fingerprint}" 2>&1 \ - | awk -F: '$1 == "uid" {print $10}' \ - | head -n1 +### Get primary UID for a given fingerprint +get_uid(){ + local fp="${1?}" + gpg --list-keys --with-colons "${fp}" 2>&1 \ + | awk -F: '$1 == "uid" {print $10}' \ + | head -n1 } -get_group_config(){ - local group=${1?} - gpg --with-colons --list-config group \ - | grep -i "^cfg:group:${group}:" \ - || die "Error: group \"${group}\" not found in ~/.gnupg/gpg.conf" +### Get primary fingerprint for given search +get_primary_fp(){ + local search="${1?}" + gpg --list-keys --with-colons "${search}" 2>&1 \ + | awk -F: '$1 == "fpr" {print $10}' \ + | head -n1 } +### Get fingerprint for a given pgp file +get_file_fp(){ + local filename="${1?}" + gpg --list-packets "${filename}" \ + | grep keyid \ + | sed 's/.*keyid //g' +} + +### Get raw gpgconf group config +group_get_config(){ + local -r config=$(gpgconf --list-options gpg | grep ^group) + printf '%s' "${config##*:}" +} + +### Add fingerprint to a given group +group_add_fp(){ + local fp=${1?} + local group_name=${2?} + local group_names=() + local member_lists=() + local name member_list config i data + + config=$(group_get_config) + while IFS=' =' read -rd, name member_list; do + group_names+=("${name:1}") + member_lists+=("$member_list") + done <<< "$config," + + printf '%s\n' "${group_names[@]}" \ + | grep -w "${group_name}" \ + || group_names+=("${group_name}") + + for i in "${!group_names[@]}"; do + [ "${group_names[$i]}" == "${group_name}" ] \ + && member_lists[$i]="${member_lists[$i]} ${fp}" + data+=$(printf '"%s = %s,' "${group_names[$i]}" "${member_lists[$i]}") + done + + echo "Adding key \"${fp}\" to group \"${group_name}\"" + printf 'group:0:%s' "${data%?}" \ + | gpgconf --change-options gpg >/dev/null 2>&1 +} + +### Get fingerprints for a given group +group_get_fps(){ + local group_name=${1?} + gpg --with-colons --list-config group \ + | grep -i "^cfg:group:${group_name}:" \ + | cut -d ':' -f4 +} + +### Check if fingerprint belongs to a given group +### Give user option to add it if they wish +group_check_fp(){ + local fp=${1?} + local group_name=${2?} + local group_fps; group_fps=$( group_get_fps "${group_name}" ) + local uid; uid=$(get_uid "${fp}") + + if [ -z "$group_fps" ] \ + || [[ "${group_fps}" != *"${fp}"* ]]; then + + cat <<-_EOF + + The following key is not a member of group "${group_name}": + + Fingerprint: ${fp} + Primary UID: ${uid} + _EOF + if ask "Add key to group \"${group_name}\" ?" "N"; then + group_add_fp "${fp}" "${group_name}" + else + return 1 + fi + fi +} + + ### Verify a file has 0-N unique valid detached signatures ### Optionally verify all signatures belong to keys in gpg alias group verify_detached() { @@ -120,39 +223,34 @@ verify_detached() { local threshold="${1}" local group="${2}" local filename="${3}" - local group_config="" local sig_count=0 - local seen_fingerprints="" - local fingerprint - local signer + local seen_fps="" + local fp + local uid for sig_filename in "${filename%.*}".*.asc; do gpg --verify "${sig_filename}" "${filename}" >/dev/null 2>&1 || { echo "Invalid signature: ${sig_filename}"; exit 1; } - fingerprint=$( \ - gpg --list-packets "${sig_filename}" \ - | grep keyid \ - | sed 's/.*keyid //g' - ) - signer=$( get_signer "${fingerprint}" ) + file_fp=$( get_file_fp "${sig_filename}" ) + fp=$( get_primary_fp "${file_fp}" ) + uid=$( get_uid "${fp}" ) - [[ "${seen_fingerprints}" == *"${fingerprint}"* ]] \ + [[ "${seen_fps}" == *"${fp}"* ]] \ && die "Duplicate signature: ${sig_filename}"; - if [ ! -z "$group" ]; then - group_config=$(get_group_config "${group}") - [[ "${group_config}" != *"${fingerprint}"* ]] \ - && die "Signature not in group \"${group}\": ${sig_filename}"; + echo "Verified detached signature by \"${uid}\"" + + if [ ! -z "${group}" ]; then + group_check_fp "${fp}" "${group}" \ + || die "Detached signing key not in group \"${group}\": ${fp}"; fi - echo "Verified detached signature by \"${signer}\"" - - seen_fingerprints="${seen_fingerprints} ${fingerprint}" + seen_fps="${seen_fps} ${fp}" ((sig_count=sig_count+1)) done - [[ "$sig_count" -ge "$threshold" ]] || \ + [[ "${sig_count}" -ge "${threshold}" ]] || \ die "Minimum detached signatures not found: ${sig_count}/${threshold}"; } @@ -163,30 +261,29 @@ verify_git(){ [ $# -eq 2 ] || die "Usage: verify_git " local threshold="${1}" local group="${2}" - local group_config="" - local seen_fingerprints="" + local seen_fps="" local sig_count=0 local depth=0 while [[ $depth != "$(git rev-list --count HEAD)" ]]; do ref=HEAD~${depth} commit=$(git log --format="%H" "$ref") - fingerprint=$(git log --format="%GP" "$ref" -n1 ) - signer=$( get_signer "${fingerprint}" ) + fp=$(git log --format="%GP" "$ref" -n1 ) + uid=$( get_uid "${fp}" ) git verify-commit HEAD~${depth} >/dev/null 2>&1\ || die "Unsigned commit: ${commit}" - if [ ! -z "$group" ]; then - group_config=$(get_group_config "${group}") - [[ "${group_config}" != *"${fingerprint}"* ]] \ - && die "Git signing key not in group \"${group}\": ${fingerprint}"; + if [[ "${seen_fps}" != *"${fp}"* ]]; then + seen_fps="${seen_fps} ${fp}" + ((sig_count=sig_count+1)) + echo "Verified git signature at depth ${depth} by \"${uid}\"" fi - [[ "${seen_fingerprints}" != *"${fingerprint}"* ]] \ - && seen_fingerprints="${seen_fingerprints} ${fingerprint}" \ - && ((sig_count=sig_count+1)) \ - && echo "Verified git signature at depth ${depth} by \"${signer}\"" + if [ ! -z "$group" ]; then + group_check_fp "${fp}" "${group}" \ + || die "Git signing key not in group \"${group}\": ${fp}" + fi [[ "${sig_count}" -ge "${threshold}" ]] && break; @@ -238,15 +335,14 @@ cmd_verify() { } cmd_add(){ - local fingerprint cmd_manifest gpg --armor --detach-sig ."${PROGRAM}"/manifest.txt >/dev/null 2>&1 - fingerprint=$( \ + local fp; fp=$( \ gpg --list-packets ."${PROGRAM}"/manifest.txt.asc \ | grep "issuer key ID" \ | sed 's/.*\([A-Z0-9]\{16\}\).*/\1/g' \ ) - mv ."${PROGRAM}"/manifest.{"txt.asc","${fingerprint}.asc"} + mv ."${PROGRAM}"/manifest.{"txt.asc","${fp}.asc"} } cmd_version() {