#!/usr/bin/bash
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2021 Frédéric Pierret (fepitre) <frederic@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -e
[ "$DEBUG" = "1" ] && set -x

usage() {
echo "Usage: $(basename "$0") [OPTIONS]...
This script verifies a file.

Options:
    --untrusted-file            Untrusted file to be verified
    --checksum-file             Checksum for file to be used for verification
    --checksum-method           Checksum method (default: sha256sum)
    --untrusted-signature-file  Signature file
    --pubkey-file               GPG public key file to be used for verification
    --output-dir                Output directory
"
}

unset OPTS GETOPT_COMPATIBLE UNTRUSTED_FILE FILE_CHECKSUM CHECKSUM_CMD UNTRUSTED_SIGNATURE_FILE SIGNATURE_URL OUTPUT_DIR

if ! OPTS=$(getopt -o hf:c:m:s:p:o: --long help,untrusted-file:,checksum-file:,checksum-cmd:,untrusted-signature-file:,pubkey-file:,output-dir: -n "$0" -- "$@"); then
    echo "ERROR: Failed while parsing options."
    exit 1
fi

eval set -- "$OPTS"

PUBKEY_FILE=()

while [[ $# -gt 0 ]]; do
    case "$1" in
        -h | --help) usage ;;
        -f | --untrusted-file ) UNTRUSTED_FILE="$2"; shift ;;
        -c | --checksum-file ) FILE_CHECKSUM="$2"; shift ;;
        -m | --checksum-cmd ) CHECKSUM_CMD="$2"; shift ;;
        -s | --untrusted-signature-file ) UNTRUSTED_SIGNATURE_FILE="$2"; shift ;;
        -p | --pubkey-file ) PUBKEY_FILE+=("$2"); shift ;;
        -o | --output-dir ) OUTPUT_DIR="$2"; shift ;;
    esac
    shift
done

if [ -z "${UNTRUSTED_FILE}" ]; then
    echo "ERROR: Please provide UNTRUSTED_FILE."
    exit 1
fi

if [ -n "${OUTPUT_DIR}" ]; then
    mkdir -p "${OUTPUT_DIR}"
else
    OUTPUT_DIR='.'
fi

UNTRUSTED_FILE_NAME="$(basename "${UNTRUSTED_FILE}")"
FILE_NAME="${UNTRUSTED_FILE_NAME#untrusted_}"

if [ -z "${FILE_CHECKSUM}" ] && [ -z "${UNTRUSTED_SIGNATURE_FILE}" ] && [ -z "${PUBKEY_FILE[*]}" ]; then
    echo "ERROR: Please provide either CHECKSUM or, UNTRUSTED_SIGNATURE_FILE and PUBKEY_FILE."
    exit 1
fi

if [ -n "${FILE_CHECKSUM}" ] && ! [ -e "${FILE_CHECKSUM}" ]; then
    echo "ERROR: Cannot find '${FILE_CHECKSUM}' checksum for '${FILE_NAME}'."
    exit 1
fi

if [ -n "${UNTRUSTED_SIGNATURE_FILE}" ] && [ -z "${PUBKEY_FILE}" ]; then
    echo "ERROR: Please provide PUBKEY_FILE to be used with UNTRUSTED_SIGNATURE_FILE."
    exit 1
fi

if [ -z "${UNTRUSTED_SIGNATURE_FILE}" ] && [ -n "${PUBKEY_FILE}" ]; then
    echo "ERROR: Please provide UNTRUSTED_SIGNATURE_FILE to be used with PUBKEY_FILE."
    exit 1
fi

if [ -n "${UNTRUSTED_SIGNATURE_FILE}" ] && [ ! -e "${PUBKEY_FILE}" ]; then
    echo "ERROR: Cannot find '${PUBKEY_FILE}' pubkey for '${UNTRUSTED_SIGNATURE_FILE}'."
    exit 1
fi

if [ -n "${FILE_CHECKSUM}" ]; then
    if [ -z "${CHECKSUM_CMD}" ]; then
        CHECKSUM_CMD=sha256sum
    fi

    # Check downloaded file with respect to provided checksum file
    "${CHECKSUM_CMD}" --status -c <(printf "%s  -\n" "$(cat "${FILE_CHECKSUM}")") \
      < "${UNTRUSTED_FILE}" || { echo "${CHECKSUM_CMD}: wrong checksum on '${FILE_NAME}'."; exit 1; }

elif [ -n "${UNTRUSTED_SIGNATURE_FILE}" ] && [ -n "${PUBKEY_FILE}" ]; then
    UNTRUSTED_SIGNATURE_FILE_NAME="$(basename "$UNTRUSTED_SIGNATURE_FILE")"
    SIGNATURE_FILE_NAME="${UNTRUSTED_SIGNATURE_FILE_NAME#untrusted_}"

    keyring_dir="$(mktemp -d)"

    # Import pubkey
    if sq toolbox keyring merge </dev/null 2>/dev/null; then
        sq toolbox keyring merge --output "$keyring_dir/keyring" "${PUBKEY_FILE[@]}"
        sq toolbox dearmor --output "$keyring_dir/keyring.gpg" "$keyring_dir/keyring"
    else
        sq keyring merge --output "$keyring_dir/keyring" "${PUBKEY_FILE[@]}"
        sq packet dearmor --output "$keyring_dir/keyring.gpg" "$keyring_dir/keyring" ||
        sq dearmor --output "$keyring_dir/keyring.gpg" "$keyring_dir/keyring"
    fi

    # Verify file, use sequoia if >= 1.2.0 (for crypto policy support),
    # otherwise gpg
    # https://gitlab.com/sequoia-pgp/sequoia-sqv/-/issues/4
    sqv_version=$(sqv --version || :)
    if printf "%s\n" "sqv 1.2.0" "$sqv_version" | sort -VC; then
        script_dir=$(dirname "$0")
        export SEQUOIA_CRYPTO_POLICY=$script_dir/sequoia-crypto-policy.toml
        sqv --keyring "$keyring_dir/keyring" "${UNTRUSTED_SIGNATURE_FILE}" "${UNTRUSTED_FILE}"
    else
        gpgv --keyring "$keyring_dir/keyring.gpg" "${UNTRUSTED_SIGNATURE_FILE}" "${UNTRUSTED_FILE}"
    fi

    # Remove 'untrusted_' prefix
    mv "${UNTRUSTED_SIGNATURE_FILE}" "${OUTPUT_DIR}/${SIGNATURE_FILE_NAME}"

    # Remove temporary keyring
    rm -rf "$keyring_dir"
else
    exit 1
fi

# Remove 'untrusted_' prefix
mv "${UNTRUSTED_FILE}" "${OUTPUT_DIR}/${FILE_NAME}"
