diff options
author | V3n3RiX <venerix@redcorelinux.org> | 2020-11-25 22:39:15 +0000 |
---|---|---|
committer | V3n3RiX <venerix@redcorelinux.org> | 2020-11-25 22:39:15 +0000 |
commit | d934827bf44b7cfcf6711964418148fa60877668 (patch) | |
tree | 0625f358789b5e015e49db139cc1dbc9be00428f /eclass/verify-sig.eclass | |
parent | 2e34d110f164bf74d55fced27fe0000201b3eec5 (diff) |
gentoo resync : 25.11.2020
Diffstat (limited to 'eclass/verify-sig.eclass')
-rw-r--r-- | eclass/verify-sig.eclass | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/eclass/verify-sig.eclass b/eclass/verify-sig.eclass new file mode 100644 index 000000000000..e3ef7f240283 --- /dev/null +++ b/eclass/verify-sig.eclass @@ -0,0 +1,271 @@ +# Copyright 2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: verify-sig.eclass +# @MAINTAINER: +# Michał Górny <mgorny@gentoo.org> +# @SUPPORTED_EAPIS: 7 +# @BLURB: Eclass to verify upstream signatures on distfiles +# @DESCRIPTION: +# verify-sig eclass provides a streamlined approach to verifying +# upstream signatures on distfiles. Its primary purpose is to permit +# developers to easily verify signatures while bumping packages. +# The eclass removes the risk of developer forgetting to perform +# the verification, or performing it incorrectly, e.g. due to additional +# keys in the local keyring. It also permits users to verify +# the developer's work. +# +# To use the eclass, start by packaging the upstream's key +# as app-crypt/openpgp-keys-*. Then inherit the eclass, add detached +# signatures to SRC_URI and set VERIFY_SIG_OPENPGP_KEY_PATH. The eclass +# provides verify-sig USE flag to toggle the verification. +# +# Example use: +# @CODE +# inherit verify-sig +# +# SRC_URI="https://example.org/${P}.tar.gz +# verify-sig? ( https://example.org/${P}.tar.gz.sig )" +# BDEPEND=" +# verify-sig? ( app-crypt/openpgp-keys-example )" +# +# VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/example.asc +# @CODE + +case "${EAPI:-0}" in + 0|1|2|3|4|5|6) + die "Unsupported EAPI=${EAPI} (obsolete) for ${ECLASS}" + ;; + 7) + ;; + *) + die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" + ;; +esac + +EXPORT_FUNCTIONS src_unpack + +if [[ ! ${_VERIFY_SIG_ECLASS} ]]; then + +IUSE="verify-sig" + +BDEPEND=" + verify-sig? ( + app-crypt/gnupg + >=app-portage/gemato-16 + )" + +# @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEY_PATH +# @DEFAULT_UNSET +# @DESCRIPTION: +# Path to key bundle used to perform the verification. This is required +# when using default src_unpack. Alternatively, the key path can be +# passed directly to the verification functions. + +# @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEYSERVER +# @DEFAULT_UNSET +# @DESCRIPTION: +# Keyserver used to refresh keys. If not specified, the keyserver +# preference from the key will be respected. If no preference +# is specified by the key, the GnuPG default will be used. + +# @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEY_REFRESH +# @USER_VARIABLE +# @DESCRIPTION: +# Attempt to refresh keys via WKD/keyserver. Set it to "yes" +# in make.conf to enable. Note that this requires working Internet +# connection. +: ${VERIFY_SIG_OPENPGP_KEY_REFRESH:=no} + +# @FUNCTION: verify-sig_verify_detached +# @USAGE: <file> <sig-file> [<key-file>] +# @DESCRIPTION: +# Read the detached signature from <sig-file> and verify <file> against +# it. <key-file> can either be passed directly, or it defaults +# to VERIFY_SIG_OPENPGP_KEY_PATH. The function dies if verification +# fails. +verify-sig_verify_detached() { + local file=${1} + local sig=${2} + local key=${3:-${VERIFY_SIG_OPENPGP_KEY_PATH}} + + [[ -n ${key} ]] || + die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" + + local extra_args=() + [[ ${VERIFY_SIG_OPENPGP_KEY_REFRESH} == yes ]] || extra_args+=( -R ) + [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]] && extra_args+=( + --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}" + ) + + # GPG upstream knows better than to follow the spec, so we can't + # override this directory. However, there is a clean fallback + # to GNUPGHOME. + addpredict /run/user + + local filename=${file##*/} + [[ ${file} == - ]] && filename='(stdin)' + einfo "Verifying ${filename} ..." + gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \ + gpg --verify "${sig}" "${file}" || + die "PGP signature verification failed" +} + +# @FUNCTION: verify-sig_verify_message +# @USAGE: <file> <output-file> [<key-file>] +# @DESCRIPTION: +# Verify that the file ('-' for stdin) contains a valid, signed PGP +# message and write the message into <output-file> ('-' for stdout). +# <key-file> can either be passed directly, or it defaults +# to VERIFY_SIG_OPENPGP_KEY_PATH. The function dies if verification +# fails. Note that using output from <output-file> is important as it +# prevents the injection of unsigned data. +verify-sig_verify_message() { + local file=${1} + local output_file=${2} + local key=${3:-${VERIFY_SIG_OPENPGP_KEY_PATH}} + + [[ -n ${key} ]] || + die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" + + local extra_args=() + [[ ${VERIFY_SIG_OPENPGP_KEY_REFRESH} == yes ]] || extra_args+=( -R ) + [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]] && extra_args+=( + --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}" + ) + + # GPG upstream knows better than to follow the spec, so we can't + # override this directory. However, there is a clean fallback + # to GNUPGHOME. + addpredict /run/user + + local filename=${file##*/} + [[ ${file} == - ]] && filename='(stdin)' + einfo "Verifying ${filename} ..." + gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \ + gpg --verify --output="${output_file}" "${file}" || + die "PGP signature verification failed" +} + +# @FUNCTION: verify-sig_verify_signed_checksums +# @USAGE: <checksum-file> <algo> <files> [<key-file>] +# @DESCRIPTION: +# Verify the checksums for all files listed in the space-separated list +# <files> (akin to ${A}) using a PGP-signed <checksum-file>. <algo> +# specified the checksum algorithm (e.g. sha256). <key-file> can either +# be passed directly, or it defaults to VERIFY_SIG_OPENPGP_KEY_PATH. +# +# The function dies if PGP verification fails, the checksum file +# contains unsigned data, one of the files do not match checksums +# or are missing from the checksum file. +verify-sig_verify_signed_checksums() { + local checksum_file=${1} + local algo=${2} + local files=() + read -r -d '' -a files <<<"${3}" + local key=${4:-${VERIFY_SIG_OPENPGP_KEY_PATH}} + + local chksum_prog chksum_len + case ${algo} in + sha256) + chksum_prog=sha256sum + chksum_len=64 + ;; + *) + die "${FUNCNAME}: unknown checksum algo ${algo}" + ;; + esac + + [[ -n ${key} ]] || + die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" + + local checksum filename junk ret=0 count=0 + while read -r checksum filename junk; do + [[ ${#checksum} -eq ${chksum_len} ]] || continue + [[ -z ${checksum//[0-9a-f]} ]] || continue + has "${filename}" "${files[@]}" || continue + [[ -z ${junk} ]] || continue + + "${chksum_prog}" -c --strict - <<<"${checksum} ${filename}" + if [[ ${?} -eq 0 ]]; then + (( count++ )) + else + ret=1 + fi + done < <(verify-sig_verify_message "${checksum_file}" - "${key}") + + [[ ${ret} -eq 0 ]] || + die "${FUNCNAME}: at least one file did not verify successfully" + [[ ${count} -eq ${#files[@]} ]] || + die "${FUNCNAME}: checksums for some of the specified files were missing" +} + +# @FUNCTION: verify-sig_src_unpack +# @DESCRIPTION: +# Default src_unpack override that verifies signatures for all +# distfiles if 'verify-sig' flag is enabled. The function dies if any +# of the signatures fails to verify or if any distfiles are not signed. +# Please write src_unpack() yourself if you need to perform partial +# verification. +verify-sig_src_unpack() { + if use verify-sig; then + local f suffix found + local distfiles=() signatures=() nosigfound=() straysigs=() + + # find all distfiles and signatures, and combine them + for f in ${A}; do + found= + for suffix in .asc .sig; do + if [[ ${f} == *${suffix} ]]; then + signatures+=( "${f}" ) + found=sig + break + else + if has "${f}${suffix}" ${A}; then + distfiles+=( "${f}" ) + found=dist+sig + break + fi + fi + done + if [[ ! ${found} ]]; then + nosigfound+=( "${f}" ) + fi + done + + # check if all distfiles are signed + if [[ ${#nosigfound[@]} -gt 0 ]]; then + eerror "The following distfiles lack detached signatures:" + for f in "${nosigfound[@]}"; do + eerror " ${f}" + done + die "Unsigned distfiles found" + fi + + # check if there are no stray signatures + for f in "${signatures[@]}"; do + if ! has "${f%.*}" "${distfiles[@]}"; then + straysigs+=( "${f}" ) + fi + done + if [[ ${#straysigs[@]} -gt 0 ]]; then + eerror "The following signatures do not match any distfiles:" + for f in "${straysigs[@]}"; do + eerror " ${f}" + done + die "Unused signatures found" + fi + + # now perform the verification + for f in "${signatures[@]}"; do + verify-sig_verify_detached \ + "${DISTDIR}/${f%.*}" "${DISTDIR}/${f}" + done + fi + + # finally, unpack the distfiles + default_src_unpack +} + +_VERIFY_SIG_ECLASS=1 +fi |