Gentoo Archives: gentoo-dev

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

Replies