Gentoo Archives: gentoo-dev

From: Anna Vyalkova <cyber+gentoo@×××××.in>
To: gentoo-dev@l.g.o
Subject: [gentoo-dev] [PATCH v4] verify-sig.eclass: add app-crypt/signify support
Date: Fri, 10 Dec 2021 05:40:54
Message-Id: 20211210054007.9317-1-cyber+gentoo@sysrq.in
In Reply to: [gentoo-dev] [PATCH] verify-sig.eclass: add app-crypt/signify support by Anna Vyalkova
1 It is useful for verifying distfiles that come from OpenBSD folks, since
2 signify produces signatures incompatible with GnuPG.
3
4 Signed-off-by: Anna Vyalkova <cyber+gentoo@×××××.in>
5 ---
6 Changed "ed25519" back to "signify"
7 eclass/verify-sig.eclass | 141 ++++++++++++++++++++++++++++++---------
8 1 file changed, 108 insertions(+), 33 deletions(-)
9
10 diff --git a/eclass/verify-sig.eclass b/eclass/verify-sig.eclass
11 index 2bc5bd5ddba..7d5f89fbc44 100644
12 --- a/eclass/verify-sig.eclass
13 +++ b/eclass/verify-sig.eclass
14 @@ -1,265 +1,340 @@
15 # Copyright 2020-2021 Gentoo Authors
16 # Distributed under the terms of the GNU General Public License v2
17
18 # @ECLASS: verify-sig.eclass
19 # @MAINTAINER:
20 # Michał Górny <mgorny@g.o>
21 # @SUPPORTED_EAPIS: 7 8
22 # @BLURB: Eclass to verify upstream signatures on distfiles
23 # @DESCRIPTION:
24 # verify-sig eclass provides a streamlined approach to verifying
25 # upstream signatures on distfiles. Its primary purpose is to permit
26 # developers to easily verify signatures while bumping packages.
27 # The eclass removes the risk of developer forgetting to perform
28 # the verification, or performing it incorrectly, e.g. due to additional
29 # keys in the local keyring. It also permits users to verify
30 # the developer's work.
31 #
32 # To use the eclass, start by packaging the upstream's key
33 # as app-crypt/openpgp-keys-*. Then inherit the eclass, add detached
34 # signatures to SRC_URI and set VERIFY_SIG_OPENPGP_KEY_PATH. The eclass
35 # provides verify-sig USE flag to toggle the verification.
36 #
37 +# If you need to use signify, you may want to copy distfiles into WORKDIR to
38 +# work around "Too many levels of symbolic links" error.
39 +# @EXAMPLE:
40 # Example use:
41 +#
42 # @CODE
43 # inherit verify-sig
44 #
45 # SRC_URI="https://example.org/${P}.tar.gz
46 # verify-sig? ( https://example.org/${P}.tar.gz.sig )"
47 # BDEPEND="
48 # verify-sig? ( app-crypt/openpgp-keys-example )"
49 #
50 # VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/example.asc
51 # @CODE
52
53 case ${EAPI} in
54 7|8) ;;
55 *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
56 esac
57
58 EXPORT_FUNCTIONS src_unpack
59
60 if [[ ! ${_VERIFY_SIG_ECLASS} ]]; then
61
62 IUSE="verify-sig"
63
64 -BDEPEND="
65 - verify-sig? (
66 - app-crypt/gnupg
67 - >=app-portage/gemato-16
68 - )"
69 +# @ECLASS-VARIABLE: VERIFY_SIG_METHOD
70 +# @PRE_INHERIT
71 +# @DESCRIPTION:
72 +# Signature verification method to use. The allowed value are:
73 +#
74 +# - openpgp -- verify PGP signatures using app-crypt/gnupg (the default)
75 +# - signify -- verify signatures with Ed25519 public key using app-crypt/signify
76 +: ${VERIFY_SIG_METHOD:=openpgp}
77 +
78 +case ${VERIFY_SIG_METHOD} in
79 + openpgp)
80 + BDEPEND="
81 + verify-sig? (
82 + app-crypt/gnupg
83 + >=app-portage/gemato-16
84 + )"
85 + ;;
86 + signify)
87 + BDEPEND="verify-sig? ( app-crypt/signify )"
88 + ;;
89 + *)
90 + die "${ECLASS}: unknown method '${VERIFY_SIG_METHOD}'"
91 + ;;
92 +esac
93
94 # @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEY_PATH
95 # @DEFAULT_UNSET
96 # @DESCRIPTION:
97 # Path to key bundle used to perform the verification. This is required
98 # when using default src_unpack. Alternatively, the key path can be
99 # passed directly to the verification functions.
100
101 # @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEYSERVER
102 # @DEFAULT_UNSET
103 # @DESCRIPTION:
104 # Keyserver used to refresh keys. If not specified, the keyserver
105 # preference from the key will be respected. If no preference
106 -# is specified by the key, the GnuPG default will be used.
107 +# is specified by the key, the GnuPG default will be used. Supported for
108 +# OpenPGP only.
109
110 # @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEY_REFRESH
111 # @USER_VARIABLE
112 # @DESCRIPTION:
113 # Attempt to refresh keys via WKD/keyserver. Set it to "yes"
114 # in make.conf to enable. Note that this requires working Internet
115 -# connection.
116 +# connection. Supported for OpenPGP only.
117 : ${VERIFY_SIG_OPENPGP_KEY_REFRESH:=no}
118
119 # @FUNCTION: verify-sig_verify_detached
120 # @USAGE: <file> <sig-file> [<key-file>]
121 # @DESCRIPTION:
122 # Read the detached signature from <sig-file> and verify <file> against
123 # it. <key-file> can either be passed directly, or it defaults
124 # to VERIFY_SIG_OPENPGP_KEY_PATH. The function dies if verification
125 # fails.
126 verify-sig_verify_detached() {
127 local file=${1}
128 local sig=${2}
129 local key=${3:-${VERIFY_SIG_OPENPGP_KEY_PATH}}
130
131 [[ -n ${key} ]] ||
132 die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset"
133
134 local extra_args=()
135 [[ ${VERIFY_SIG_OPENPGP_KEY_REFRESH} == yes ]] || extra_args+=( -R )
136 - [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]] && extra_args+=(
137 - --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}"
138 - )
139 + if [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]]; then
140 + [[ ${VERIFY_SIG_METHOD} == openpgp ]] ||
141 + die "${FUNCNAME}: VERIFY_SIG_OPENPGP_KEYSERVER is not supported"
142 +
143 + extra_args+=(
144 + --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}"
145 + )
146 + fi
147
148 # GPG upstream knows better than to follow the spec, so we can't
149 # override this directory. However, there is a clean fallback
150 # to GNUPGHOME.
151 addpredict /run/user
152
153 local filename=${file##*/}
154 [[ ${file} == - ]] && filename='(stdin)'
155 einfo "Verifying ${filename} ..."
156 - gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
157 - gpg --verify "${sig}" "${file}" ||
158 - die "PGP signature verification failed"
159 + case ${VERIFY_SIG_METHOD} in
160 + openpgp)
161 + gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
162 + gpg --verify "${sig}" "${file}" ||
163 + die "PGP signature verification failed"
164 + ;;
165 + signify)
166 + signify -V -p "${key}" -m "${file}" -x "${sig}" ||
167 + die "Signify signature verification failed"
168 + ;;
169 + esac
170 }
171
172 # @FUNCTION: verify-sig_verify_message
173 # @USAGE: <file> <output-file> [<key-file>]
174 # @DESCRIPTION:
175 # Verify that the file ('-' for stdin) contains a valid, signed PGP
176 # message and write the message into <output-file> ('-' for stdout).
177 # <key-file> can either be passed directly, or it defaults
178 # to VERIFY_SIG_OPENPGP_KEY_PATH. The function dies if verification
179 # fails. Note that using output from <output-file> is important as it
180 # prevents the injection of unsigned data.
181 verify-sig_verify_message() {
182 local file=${1}
183 local output_file=${2}
184 local key=${3:-${VERIFY_SIG_OPENPGP_KEY_PATH}}
185
186 [[ -n ${key} ]] ||
187 die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset"
188
189 local extra_args=()
190 [[ ${VERIFY_SIG_OPENPGP_KEY_REFRESH} == yes ]] || extra_args+=( -R )
191 - [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]] && extra_args+=(
192 - --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}"
193 - )
194 + if [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]]; then
195 + [[ ${VERIFY_SIG_METHOD} == openpgp ]] ||
196 + die "${FUNCNAME}: VERIFY_SIG_OPENPGP_KEYSERVER is not supported"
197 +
198 + extra_args+=(
199 + --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}"
200 + )
201 + fi
202
203 # GPG upstream knows better than to follow the spec, so we can't
204 # override this directory. However, there is a clean fallback
205 # to GNUPGHOME.
206 addpredict /run/user
207
208 local filename=${file##*/}
209 [[ ${file} == - ]] && filename='(stdin)'
210 einfo "Verifying ${filename} ..."
211 - gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
212 - gpg --verify --output="${output_file}" "${file}" ||
213 - die "PGP signature verification failed"
214 + case ${VERIFY_SIG_METHOD} in
215 + openpgp)
216 + gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \
217 + gpg --verify --output="${output_file}" "${file}" ||
218 + die "PGP signature verification failed"
219 + ;;
220 + signify)
221 + signify -V -e -p "${key}" -m "${output_file}" -x "${file}" ||
222 + die "Signify signature verification failed"
223 + ;;
224 + esac
225 }
226
227 -# @FUNCTION: verify-sig_verify_signed_checksums
228 +# @FUNCTION: _gpg_verify_signed_checksums
229 +# @INTERNAL
230 # @USAGE: <checksum-file> <algo> <files> [<key-file>]
231 # @DESCRIPTION:
232 -# Verify the checksums for all files listed in the space-separated list
233 -# <files> (akin to ${A}) using a PGP-signed <checksum-file>. <algo>
234 -# specified the checksum algorithm (e.g. sha256). <key-file> can either
235 -# be passed directly, or it defaults to VERIFY_SIG_OPENPGP_KEY_PATH.
236 -#
237 -# The function dies if PGP verification fails, the checksum file
238 -# contains unsigned data, one of the files do not match checksums
239 -# or are missing from the checksum file.
240 -verify-sig_verify_signed_checksums() {
241 +# GnuPG-specific function to verify a signed checksums list.
242 +_gpg_verify_signed_checksums() {
243 local checksum_file=${1}
244 local algo=${2}
245 local files=()
246 read -r -d '' -a files <<<"${3}"
247 local key=${4:-${VERIFY_SIG_OPENPGP_KEY_PATH}}
248 -
249 local chksum_prog chksum_len
250 +
251 case ${algo} in
252 sha256)
253 chksum_prog=sha256sum
254 chksum_len=64
255 ;;
256 *)
257 die "${FUNCNAME}: unknown checksum algo ${algo}"
258 ;;
259 esac
260
261 - [[ -n ${key} ]] ||
262 - die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset"
263 -
264 local checksum filename junk ret=0 count=0
265 while read -r checksum filename junk; do
266 [[ ${#checksum} -eq ${chksum_len} ]] || continue
267 [[ -z ${checksum//[0-9a-f]} ]] || continue
268 has "${filename}" "${files[@]}" || continue
269 [[ -z ${junk} ]] || continue
270
271 "${chksum_prog}" -c --strict - <<<"${checksum} ${filename}"
272 if [[ ${?} -eq 0 ]]; then
273 (( count++ ))
274 else
275 ret=1
276 fi
277 done < <(verify-sig_verify_message "${checksum_file}" - "${key}")
278
279 [[ ${ret} -eq 0 ]] ||
280 die "${FUNCNAME}: at least one file did not verify successfully"
281 [[ ${count} -eq ${#files[@]} ]] ||
282 die "${FUNCNAME}: checksums for some of the specified files were missing"
283 }
284
285 +# @FUNCTION: verify-sig_verify_signed_checksums
286 +# @USAGE: <checksum-file> <algo> <files> [<key-file>]
287 +# @DESCRIPTION:
288 +# Verify the checksums for all files listed in the space-separated list
289 +# <files> (akin to ${A}) using a signed <checksum-file>. <algo> specifies
290 +# the checksum algorithm (e.g. sha256). <key-file> can either be passed
291 +# directly, or it defaults to VERIFY_SIG_OPENPGP_KEY_PATH.
292 +#
293 +# The function dies if signature verification fails, the checksum file
294 +# contains unsigned data, one of the files do not match checksums or
295 +# are missing from the checksum file.
296 +verify-sig_verify_signed_checksums() {
297 + local checksum_file=${1}
298 + local algo=${2}
299 + local files=()
300 + read -r -d '' -a files <<<"${3}"
301 + local key=${4:-${VERIFY_SIG_OPENPGP_KEY_PATH}}
302 +
303 + [[ -n ${key} ]] ||
304 + die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset"
305 +
306 + case ${VERIFY_SIG_METHOD} in
307 + openpgp)
308 + _gpg_verify_signed_checksums \
309 + "${checksum_file}" "${algo}" "${files[@]}" "${key}"
310 + ;;
311 + signify)
312 + signify -C -p "${key}" \
313 + -x "${checksum_file}" "${files[@]}" ||
314 + die "Signify signature verification failed"
315 + ;;
316 + esac
317 +}
318 +
319 # @FUNCTION: verify-sig_src_unpack
320 # @DESCRIPTION:
321 # Default src_unpack override that verifies signatures for all
322 # distfiles if 'verify-sig' flag is enabled. The function dies if any
323 # of the signatures fails to verify or if any distfiles are not signed.
324 # Please write src_unpack() yourself if you need to perform partial
325 # verification.
326 verify-sig_src_unpack() {
327 if use verify-sig; then
328 local f suffix found
329 local distfiles=() signatures=() nosigfound=() straysigs=()
330
331 # find all distfiles and signatures, and combine them
332 for f in ${A}; do
333 found=
334 for suffix in .asc .sig; do
335 if [[ ${f} == *${suffix} ]]; then
336 signatures+=( "${f}" )
337 found=sig
338 break
339 else
340 if has "${f}${suffix}" ${A}; then
341 distfiles+=( "${f}" )
342 found=dist+sig
343 break
344 fi
345 fi
346 done
347 if [[ ! ${found} ]]; then
348 nosigfound+=( "${f}" )
349 fi
350 done
351
352 # check if all distfiles are signed
353 if [[ ${#nosigfound[@]} -gt 0 ]]; then
354 eerror "The following distfiles lack detached signatures:"
355 for f in "${nosigfound[@]}"; do
356 eerror " ${f}"
357 done
358 die "Unsigned distfiles found"
359 fi
360
361 # check if there are no stray signatures
362 for f in "${signatures[@]}"; do
363 if ! has "${f%.*}" "${distfiles[@]}"; then
364 straysigs+=( "${f}" )
365 fi
366 done
367 if [[ ${#straysigs[@]} -gt 0 ]]; then
368 eerror "The following signatures do not match any distfiles:"
369 for f in "${straysigs[@]}"; do
370 eerror " ${f}"
371 done
372 die "Unused signatures found"
373 fi
374
375 # now perform the verification
376 for f in "${signatures[@]}"; do
377 verify-sig_verify_detached \
378 "${DISTDIR}/${f%.*}" "${DISTDIR}/${f}"
379 done
380 fi
381
382 # finally, unpack the distfiles
383 default_src_unpack
384 }
385
386 _VERIFY_SIG_ECLASS=1
387 fi
388 --
389 2.34.1