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