Gentoo Archives: gentoo-dev

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

Replies

Subject Author
Re: [gentoo-dev] [PATCH] verify-sig.eclass: add app-crypt/signify support "Haelwenn (lanodan) Monnier" <contact@×××××××××.me>
[gentoo-dev] [PATCH v2] verify-sig.eclass: add app-crypt/signify support Anna Vyalkova <cyber+gentoo@×××××.in>
[gentoo-dev] [PATCH v3] verify-sig.eclass: add app-crypt/signify support Anna Vyalkova <cyber+gentoo@×××××.in>
[gentoo-dev] [PATCH v4] verify-sig.eclass: add app-crypt/signify support Anna Vyalkova <cyber+gentoo@×××××.in>