On Thu, 2021-12-09 at 14:42 +0500, Anna Vyalkova wrote:
> It is useful for verifying distfiles that come from OpenBSD folks, since
> signify produces signatures incompatible with GnuPG.

For the record, I don't like the fact that OpenBSD reinvents the wheel
but I'm not going to oppose the patch.

> 
> Signed-off-by: Anna Vyalkova <[email protected]>
> ---
> Changes from previous patch:
> - wording
> 
>  eclass/verify-sig.eclass | 138 +++++++++++++++++++++++++++++----------
>  1 file changed, 105 insertions(+), 33 deletions(-)
> 
> diff --git a/eclass/verify-sig.eclass b/eclass/verify-sig.eclass
> index 2bc5bd5ddba..b3e6eb131a4 100644
> --- a/eclass/verify-sig.eclass
> +++ b/eclass/verify-sig.eclass
> @@ -1,265 +1,337 @@
>  # Copyright 2020-2021 Gentoo Authors
>  # Distributed under the terms of the GNU General Public License v2
>  
>  # @ECLASS: verify-sig.eclass
>  # @MAINTAINER:
>  # Michał Górny <[email protected]>
>  # @SUPPORTED_EAPIS: 7 8
>  # @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.
>  #
> +# If you need to use signify, you may want to copy distfiles into WORKDIR to
> +# work around "Too many levels of symbolic links" error.
> +# @EXAMPLE:
>  # 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=${BROOT}/usr/share/openpgp-keys/example.asc
>  # @CODE
>  
>  case ${EAPI} in
>       7|8) ;;
>       *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
>  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_IMPL
> +# @PRE_INHERIT
> +# @DESCRIPTION:
> +# Signature verification utility to use.  Valid options: "gnupg" and 
> "signify".
> +: ${VERIFY_SIG_IMPL:=gnupg}

Make this "openpgp", please.  A future version may not be using GnuPG
anymore but the standard will remain.

Does "signify" have some backing standard name too?

> +
> +case ${VERIFY_SIG_IMPL} in
> +     gnupg)
> +             BDEPEND="
> +                     verify-sig? (
> +                             app-crypt/gnupg
> +                             >=app-portage/gemato-16
> +                     )"
> +             ;;
> +     signify)
> +             BDEPEND="verify-sig? ( app-crypt/signify )"
> +             ;;
> +     *)
> +             die "${ECLASS}: unknown implementation '${VERIFY_SIG_IMPL}'"
> +             ;;
> +esac
>  
>  # @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.
> +# is specified by the key, the GnuPG default will be used.  Supported for 
> GnuPG
> +# only.
>  
>  # @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.
> +# connection.  Supported for GnuPG only.
>  : ${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}"
> -     )
> +     if [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]]; then
> +             [[ ${VERIFY_SIG_IMPL} == gnupg ]] ||
> +                     die "${FUNCNAME}: VERIFY_SIG_OPENPGP_KEYSERVER is not 
> supported"
> +
> +             extra_args+=(
> +                     --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}"
> +             )
> +     fi
>  
>       # 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"
> +     case ${VERIFY_SIG_IMPL} in
> +             gnupg)
> +                     gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
> +                             gpg --verify "${sig}" "${file}" ||
> +                             die "PGP signature verification failed"
> +                     ;;
> +             signify)
> +                     signify -V -p "${key}" -m "${file}" -x "${sig}" ||
> +                             die "Signify signature verification failed"
> +                     ;;
> +     esac
>  }
>  
>  # @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}"
> -     )
> +     if [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]]; then
> +             [[ ${VERIFY_SIG_IMPL} == gnupg ]] ||
> +                     die "${FUNCNAME}: VERIFY_SIG_OPENPGP_KEYSERVER is not 
> supported"
> +
> +             extra_args+=(
> +                     --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}"
> +             )
> +     fi
>  
>       # 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"
> +     case ${VERIFY_SIG_IMPL} in
> +             gnupg)
> +                     gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
> +                             gpg --verify --output="${output_file}" 
> "${file}" ||
> +                             die "PGP signature verification failed"
> +                     ;;
> +             signify)
> +                     signify -V -e -p "${key}" -m "${output_file}" -x 
> "${file}" ||
> +                             die "Signify signature verification failed"
> +                     ;;
> +     esac
>  }
>  
> -# @FUNCTION: verify-sig_verify_signed_checksums
> +# @FUNCTION: _gpg_verify_signed_checksums
> +# @INTERNAL
>  # @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() {
> +# GnuPG-specific function to verify a signed checksums list.
> +_gpg_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_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 signed <checksum-file>.  <algo> specifies
> +# 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 signature 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}}
> +
> +     [[ -n ${key} ]] ||
> +             die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH 
> unset"
> +
> +     case ${VERIFY_SIG_IMPL} in
> +             gnupg)
> +                     _gpg_verify_signed_checksums \
> +                             "${checksum_file}" "${algo}" "${files[@]}" 
> "${key}"
> +                     ;;
> +             signify)
> +                     signify -C -p "${key}" \
> +                             -x "${checksum_file}" "${files[@]}" ||
> +                             die "Signify signature verification failed"
> +                     ;;
> +     esac
> +}
> +
>  # @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

-- 
Best regards,
Michał Górny


Reply via email to