Björn Persson 3da0ad5
#!/bin/bash
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# Copyright 2018 B. Persson, Bjorn@Rombobeorn.se
Björn Persson 3da0ad5
#
Björn Persson 3da0ad5
# This material is provided as is, with absolutely no warranty expressed
Björn Persson 3da0ad5
# or implied. Any use is at your own risk.
Björn Persson 3da0ad5
#
Björn Persson 3da0ad5
# Permission is hereby granted to use or copy this shellscript
Björn Persson 3da0ad5
# for any purpose, provided the above notices are retained on all copies.
Björn Persson 3da0ad5
# Permission to modify the code and to distribute modified code is granted,
Björn Persson 3da0ad5
# provided the above notices are retained, and a notice that the code was
Björn Persson 3da0ad5
# modified is included with the above copyright notice.
Björn Persson 3da0ad5
Björn Persson 3da0ad5
Björn Persson 3da0ad5
function print_help {
Björn Persson 3da0ad5
    cat <<'EOF'
Björn Persson 3da0ad5
Usage: gpgverify --keyring=<pathname> --signature=<pathname> --data=<pathname>
Björn Persson 3da0ad5
Björn Persson 3da0ad5
gpgverify is a wrapper around gpgv designed for easy and safe scripting. It
Björn Persson 3da0ad5
verifies a file against a detached OpenPGP signature and a keyring. The keyring
Björn Persson 3da0ad5
shall contain all the keys that are trusted to certify the authenticity of the
Björn Persson 3da0ad5
file, and must not contain any untrusted keys.
Björn Persson 3da0ad5
Björn Persson 3da0ad5
The differences, compared to invoking gpgv directly, are that gpgverify accepts
Björn Persson 3da0ad5
the keyring in either ASCII-armored or unarmored form, and that it will not
Björn Persson 3da0ad5
accidentally use a default keyring in addition to the specified one.
Björn Persson 3da0ad5
Björn Persson 3da0ad5
Parameters:
Björn Persson 3da0ad5
  --keyring=<pathname>    keyring with all the trusted keys and no others
Björn Persson 3da0ad5
  --signature=<pathname>  detached signature to verify
Björn Persson 3da0ad5
  --data=<pathname>       file to verify against the signature
Björn Persson 3da0ad5
EOF
Björn Persson 3da0ad5
}
Björn Persson 3da0ad5
Björn Persson 3da0ad5
Björn Persson 3da0ad5
fatal_error() {
Björn Persson 3da0ad5
    message="$1"  # an error message
Björn Persson 3da0ad5
    status=$2     # a number to use as the exit code
Björn Persson 3da0ad5
    echo "gpgverify: $message" >&2
Björn Persson 3da0ad5
    exit $status
Björn Persson 3da0ad5
}
Björn Persson 3da0ad5
Björn Persson 3da0ad5
Björn Persson 3da0ad5
require_parameter() {
Björn Persson 3da0ad5
    term="$1"   # a term for a required parameter
Björn Persson 3da0ad5
    value="$2"  # Complain and terminate if this value is empty.
Björn Persson 3da0ad5
    if test -z "${value}" ; then
Björn Persson 3da0ad5
        fatal_error "No ${term} was provided." 2
Björn Persson 3da0ad5
    fi
Björn Persson 3da0ad5
}
Björn Persson 3da0ad5
Björn Persson 3da0ad5
Björn Persson 3da0ad5
check_status() {
Björn Persson 3da0ad5
    action="$1"  # a string that describes the action that was attempted
Björn Persson 3da0ad5
    status=$2    # the exit code of the command
Björn Persson 3da0ad5
    if test $status -ne 0 ; then
Björn Persson 3da0ad5
        fatal_error "$action failed." $status
Björn Persson 3da0ad5
    fi
Björn Persson 3da0ad5
}
Björn Persson 3da0ad5
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# Parse the command line.
Björn Persson 3da0ad5
keyring=
Björn Persson 3da0ad5
signature=
Björn Persson 3da0ad5
data=
Björn Persson 3da0ad5
for parameter in "$@" ; do
Björn Persson 3da0ad5
    case "${parameter}" in
Björn Persson 3da0ad5
        (--help)
Björn Persson 3da0ad5
            print_help
Björn Persson 3da0ad5
            exit
Björn Persson 3da0ad5
            ;;
Björn Persson 3da0ad5
        (--keyring=*)
Björn Persson 3da0ad5
            keyring="${parameter#*=}"
Björn Persson 3da0ad5
            ;;
Björn Persson 3da0ad5
        (--signature=*)
Björn Persson 3da0ad5
            signature="${parameter#*=}"
Björn Persson 3da0ad5
            ;;
Björn Persson 3da0ad5
        (--data=*)
Björn Persson 3da0ad5
            data="${parameter#*=}"
Björn Persson 3da0ad5
            ;;
Björn Persson 3da0ad5
        (*)
Björn Persson 3da0ad5
            fatal_error "Unknown parameter: \"${parameter}\"" 2
Björn Persson 3da0ad5
            ;;
Björn Persson 3da0ad5
    esac
Björn Persson 3da0ad5
done
Björn Persson 3da0ad5
require_parameter 'keyring' "${keyring}"
Björn Persson 3da0ad5
require_parameter 'signature' "${signature}"
Björn Persson 3da0ad5
require_parameter 'data file' "${data}"
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# Make a temporary working directory.
Björn Persson 3da0ad5
workdir="$(mktemp --directory)"
Björn Persson 3da0ad5
check_status 'Making a temporary directory' $?
Björn Persson 3da0ad5
workring="${workdir}/keyring.gpg"
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# Decode any ASCII armor on the keyring. This is harmless if the keyring isn't
Björn Persson 3da0ad5
# ASCII-armored.
Björn Persson 3da0ad5
gpg2 --homedir="${workdir}" --yes --output="${workring}" --dearmor "${keyring}"
Björn Persson 3da0ad5
check_status 'Decoding the keyring' $?
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# Verify the signature using the decoded keyring.
Björn Persson 3da0ad5
gpgv2 --homedir="${workdir}" --keyring="${workring}" "${signature}" "${data}"
Björn Persson 3da0ad5
check_status 'Signature verification' $?
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# (--homedir isn't actually necessary. --dearmor processes only the input file,
Björn Persson 3da0ad5
# and if --keyring is used and contains a slash, then gpgv2 uses only that
Björn Persson 3da0ad5
# keyring. Thus neither command will look for a default keyring, but --homedir
Björn Persson 3da0ad5
# makes extra double sure that no default keyring will be touched in case
Björn Persson 3da0ad5
# another version of GPG works differently.)
Björn Persson 3da0ad5
Björn Persson 3da0ad5
# Clean up. (This is not done in case of an error that may need inspection.)
Björn Persson 3da0ad5
rm --recursive --force ${workdir}