1 |
On Thu, 2021-12-09 at 14:42 +0500, Anna Vyalkova wrote: |
2 |
> It is useful for verifying distfiles that come from OpenBSD folks, since |
3 |
> signify produces signatures incompatible with GnuPG. |
4 |
|
5 |
For the record, I don't like the fact that OpenBSD reinvents the wheel |
6 |
but I'm not going to oppose the patch. |
7 |
|
8 |
> |
9 |
> Signed-off-by: Anna Vyalkova <cyber+gentoo@×××××.in> |
10 |
> --- |
11 |
> Changes from previous patch: |
12 |
> - wording |
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..b3e6eb131a4 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 |
> +# Signature verification utility to use. Valid options: "gnupg" and "signify". |
80 |
> +: ${VERIFY_SIG_IMPL:=gnupg} |
81 |
|
82 |
Make this "openpgp", please. A future version may not be using GnuPG |
83 |
anymore but the standard will remain. |
84 |
|
85 |
Does "signify" have some backing standard name too? |
86 |
|
87 |
> + |
88 |
> +case ${VERIFY_SIG_IMPL} in |
89 |
> + gnupg) |
90 |
> + BDEPEND=" |
91 |
> + verify-sig? ( |
92 |
> + app-crypt/gnupg |
93 |
> + >=app-portage/gemato-16 |
94 |
> + )" |
95 |
> + ;; |
96 |
> + signify) |
97 |
> + BDEPEND="verify-sig? ( app-crypt/signify )" |
98 |
> + ;; |
99 |
> + *) |
100 |
> + die "${ECLASS}: unknown implementation '${VERIFY_SIG_IMPL}'" |
101 |
> + ;; |
102 |
> +esac |
103 |
> |
104 |
> # @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEY_PATH |
105 |
> # @DEFAULT_UNSET |
106 |
> # @DESCRIPTION: |
107 |
> # Path to key bundle used to perform the verification. This is required |
108 |
> # when using default src_unpack. Alternatively, the key path can be |
109 |
> # passed directly to the verification functions. |
110 |
> |
111 |
> # @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEYSERVER |
112 |
> # @DEFAULT_UNSET |
113 |
> # @DESCRIPTION: |
114 |
> # Keyserver used to refresh keys. If not specified, the keyserver |
115 |
> # preference from the key will be respected. If no preference |
116 |
> -# is specified by the key, the GnuPG default will be used. |
117 |
> +# is specified by the key, the GnuPG default will be used. Supported for GnuPG |
118 |
> +# only. |
119 |
> |
120 |
> # @ECLASS-VARIABLE: VERIFY_SIG_OPENPGP_KEY_REFRESH |
121 |
> # @USER_VARIABLE |
122 |
> # @DESCRIPTION: |
123 |
> # Attempt to refresh keys via WKD/keyserver. Set it to "yes" |
124 |
> # in make.conf to enable. Note that this requires working Internet |
125 |
> -# connection. |
126 |
> +# connection. Supported for GnuPG only. |
127 |
> : ${VERIFY_SIG_OPENPGP_KEY_REFRESH:=no} |
128 |
> |
129 |
> # @FUNCTION: verify-sig_verify_detached |
130 |
> # @USAGE: <file> <sig-file> [<key-file>] |
131 |
> # @DESCRIPTION: |
132 |
> # Read the detached signature from <sig-file> and verify <file> against |
133 |
> # it. <key-file> can either be passed directly, or it defaults |
134 |
> # to VERIFY_SIG_OPENPGP_KEY_PATH. The function dies if verification |
135 |
> # fails. |
136 |
> verify-sig_verify_detached() { |
137 |
> local file=${1} |
138 |
> local sig=${2} |
139 |
> local key=${3:-${VERIFY_SIG_OPENPGP_KEY_PATH}} |
140 |
> |
141 |
> [[ -n ${key} ]] || |
142 |
> die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" |
143 |
> |
144 |
> local extra_args=() |
145 |
> [[ ${VERIFY_SIG_OPENPGP_KEY_REFRESH} == yes ]] || extra_args+=( -R ) |
146 |
> - [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]] && extra_args+=( |
147 |
> - --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}" |
148 |
> - ) |
149 |
> + if [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]]; then |
150 |
> + [[ ${VERIFY_SIG_IMPL} == gnupg ]] || |
151 |
> + die "${FUNCNAME}: VERIFY_SIG_OPENPGP_KEYSERVER is not supported" |
152 |
> + |
153 |
> + extra_args+=( |
154 |
> + --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}" |
155 |
> + ) |
156 |
> + fi |
157 |
> |
158 |
> # GPG upstream knows better than to follow the spec, so we can't |
159 |
> # override this directory. However, there is a clean fallback |
160 |
> # to GNUPGHOME. |
161 |
> addpredict /run/user |
162 |
> |
163 |
> local filename=${file##*/} |
164 |
> [[ ${file} == - ]] && filename='(stdin)' |
165 |
> einfo "Verifying ${filename} ..." |
166 |
> - gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \ |
167 |
> - gpg --verify "${sig}" "${file}" || |
168 |
> - die "PGP signature verification failed" |
169 |
> + case ${VERIFY_SIG_IMPL} in |
170 |
> + gnupg) |
171 |
> + gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \ |
172 |
> + gpg --verify "${sig}" "${file}" || |
173 |
> + die "PGP signature verification failed" |
174 |
> + ;; |
175 |
> + signify) |
176 |
> + signify -V -p "${key}" -m "${file}" -x "${sig}" || |
177 |
> + die "Signify signature verification failed" |
178 |
> + ;; |
179 |
> + esac |
180 |
> } |
181 |
> |
182 |
> # @FUNCTION: verify-sig_verify_message |
183 |
> # @USAGE: <file> <output-file> [<key-file>] |
184 |
> # @DESCRIPTION: |
185 |
> # Verify that the file ('-' for stdin) contains a valid, signed PGP |
186 |
> # message and write the message into <output-file> ('-' for stdout). |
187 |
> # <key-file> can either be passed directly, or it defaults |
188 |
> # to VERIFY_SIG_OPENPGP_KEY_PATH. The function dies if verification |
189 |
> # fails. Note that using output from <output-file> is important as it |
190 |
> # prevents the injection of unsigned data. |
191 |
> verify-sig_verify_message() { |
192 |
> local file=${1} |
193 |
> local output_file=${2} |
194 |
> local key=${3:-${VERIFY_SIG_OPENPGP_KEY_PATH}} |
195 |
> |
196 |
> [[ -n ${key} ]] || |
197 |
> die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" |
198 |
> |
199 |
> local extra_args=() |
200 |
> [[ ${VERIFY_SIG_OPENPGP_KEY_REFRESH} == yes ]] || extra_args+=( -R ) |
201 |
> - [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]] && extra_args+=( |
202 |
> - --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}" |
203 |
> - ) |
204 |
> + if [[ -n ${VERIFY_SIG_OPENPGP_KEYSERVER+1} ]]; then |
205 |
> + [[ ${VERIFY_SIG_IMPL} == gnupg ]] || |
206 |
> + die "${FUNCNAME}: VERIFY_SIG_OPENPGP_KEYSERVER is not supported" |
207 |
> + |
208 |
> + extra_args+=( |
209 |
> + --keyserver "${VERIFY_SIG_OPENPGP_KEYSERVER}" |
210 |
> + ) |
211 |
> + fi |
212 |
> |
213 |
> # GPG upstream knows better than to follow the spec, so we can't |
214 |
> # override this directory. However, there is a clean fallback |
215 |
> # to GNUPGHOME. |
216 |
> addpredict /run/user |
217 |
> |
218 |
> local filename=${file##*/} |
219 |
> [[ ${file} == - ]] && filename='(stdin)' |
220 |
> einfo "Verifying ${filename} ..." |
221 |
> - gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \ |
222 |
> - gpg --verify --output="${output_file}" "${file}" || |
223 |
> - die "PGP signature verification failed" |
224 |
> + case ${VERIFY_SIG_IMPL} in |
225 |
> + gnupg) |
226 |
> + gemato gpg-wrap -K "${key}" "${extra_args[@]}" -- \ |
227 |
> + gpg --verify --output="${output_file}" "${file}" || |
228 |
> + die "PGP signature verification failed" |
229 |
> + ;; |
230 |
> + signify) |
231 |
> + signify -V -e -p "${key}" -m "${output_file}" -x "${file}" || |
232 |
> + die "Signify signature verification failed" |
233 |
> + ;; |
234 |
> + esac |
235 |
> } |
236 |
> |
237 |
> -# @FUNCTION: verify-sig_verify_signed_checksums |
238 |
> +# @FUNCTION: _gpg_verify_signed_checksums |
239 |
> +# @INTERNAL |
240 |
> # @USAGE: <checksum-file> <algo> <files> [<key-file>] |
241 |
> # @DESCRIPTION: |
242 |
> -# Verify the checksums for all files listed in the space-separated list |
243 |
> -# <files> (akin to ${A}) using a PGP-signed <checksum-file>. <algo> |
244 |
> -# specified the checksum algorithm (e.g. sha256). <key-file> can either |
245 |
> -# be passed directly, or it defaults to VERIFY_SIG_OPENPGP_KEY_PATH. |
246 |
> -# |
247 |
> -# The function dies if PGP verification fails, the checksum file |
248 |
> -# contains unsigned data, one of the files do not match checksums |
249 |
> -# or are missing from the checksum file. |
250 |
> -verify-sig_verify_signed_checksums() { |
251 |
> +# GnuPG-specific function to verify a signed checksums list. |
252 |
> +_gpg_verify_signed_checksums() { |
253 |
> local checksum_file=${1} |
254 |
> local algo=${2} |
255 |
> local files=() |
256 |
> read -r -d '' -a files <<<"${3}" |
257 |
> local key=${4:-${VERIFY_SIG_OPENPGP_KEY_PATH}} |
258 |
> - |
259 |
> local chksum_prog chksum_len |
260 |
> + |
261 |
> case ${algo} in |
262 |
> sha256) |
263 |
> chksum_prog=sha256sum |
264 |
> chksum_len=64 |
265 |
> ;; |
266 |
> *) |
267 |
> die "${FUNCNAME}: unknown checksum algo ${algo}" |
268 |
> ;; |
269 |
> esac |
270 |
> |
271 |
> - [[ -n ${key} ]] || |
272 |
> - die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" |
273 |
> - |
274 |
> local checksum filename junk ret=0 count=0 |
275 |
> while read -r checksum filename junk; do |
276 |
> [[ ${#checksum} -eq ${chksum_len} ]] || continue |
277 |
> [[ -z ${checksum//[0-9a-f]} ]] || continue |
278 |
> has "${filename}" "${files[@]}" || continue |
279 |
> [[ -z ${junk} ]] || continue |
280 |
> |
281 |
> "${chksum_prog}" -c --strict - <<<"${checksum} ${filename}" |
282 |
> if [[ ${?} -eq 0 ]]; then |
283 |
> (( count++ )) |
284 |
> else |
285 |
> ret=1 |
286 |
> fi |
287 |
> done < <(verify-sig_verify_message "${checksum_file}" - "${key}") |
288 |
> |
289 |
> [[ ${ret} -eq 0 ]] || |
290 |
> die "${FUNCNAME}: at least one file did not verify successfully" |
291 |
> [[ ${count} -eq ${#files[@]} ]] || |
292 |
> die "${FUNCNAME}: checksums for some of the specified files were missing" |
293 |
> } |
294 |
> |
295 |
> +# @FUNCTION: verify-sig_verify_signed_checksums |
296 |
> +# @USAGE: <checksum-file> <algo> <files> [<key-file>] |
297 |
> +# @DESCRIPTION: |
298 |
> +# Verify the checksums for all files listed in the space-separated list |
299 |
> +# <files> (akin to ${A}) using a signed <checksum-file>. <algo> specifies |
300 |
> +# the checksum algorithm (e.g. sha256). <key-file> can either be passed |
301 |
> +# directly, or it defaults to VERIFY_SIG_OPENPGP_KEY_PATH. |
302 |
> +# |
303 |
> +# The function dies if signature verification fails, the checksum file |
304 |
> +# contains unsigned data, one of the files do not match checksums or |
305 |
> +# are missing from the checksum file. |
306 |
> +verify-sig_verify_signed_checksums() { |
307 |
> + local checksum_file=${1} |
308 |
> + local algo=${2} |
309 |
> + local files=() |
310 |
> + read -r -d '' -a files <<<"${3}" |
311 |
> + local key=${4:-${VERIFY_SIG_OPENPGP_KEY_PATH}} |
312 |
> + |
313 |
> + [[ -n ${key} ]] || |
314 |
> + die "${FUNCNAME}: no key passed and VERIFY_SIG_OPENPGP_KEY_PATH unset" |
315 |
> + |
316 |
> + case ${VERIFY_SIG_IMPL} in |
317 |
> + gnupg) |
318 |
> + _gpg_verify_signed_checksums \ |
319 |
> + "${checksum_file}" "${algo}" "${files[@]}" "${key}" |
320 |
> + ;; |
321 |
> + signify) |
322 |
> + signify -C -p "${key}" \ |
323 |
> + -x "${checksum_file}" "${files[@]}" || |
324 |
> + die "Signify signature verification failed" |
325 |
> + ;; |
326 |
> + esac |
327 |
> +} |
328 |
> + |
329 |
> # @FUNCTION: verify-sig_src_unpack |
330 |
> # @DESCRIPTION: |
331 |
> # Default src_unpack override that verifies signatures for all |
332 |
> # distfiles if 'verify-sig' flag is enabled. The function dies if any |
333 |
> # of the signatures fails to verify or if any distfiles are not signed. |
334 |
> # Please write src_unpack() yourself if you need to perform partial |
335 |
> # verification. |
336 |
> verify-sig_src_unpack() { |
337 |
> if use verify-sig; then |
338 |
> local f suffix found |
339 |
> local distfiles=() signatures=() nosigfound=() straysigs=() |
340 |
> |
341 |
> # find all distfiles and signatures, and combine them |
342 |
> for f in ${A}; do |
343 |
> found= |
344 |
> for suffix in .asc .sig; do |
345 |
> if [[ ${f} == *${suffix} ]]; then |
346 |
> signatures+=( "${f}" ) |
347 |
> found=sig |
348 |
> break |
349 |
> else |
350 |
> if has "${f}${suffix}" ${A}; then |
351 |
> distfiles+=( "${f}" ) |
352 |
> found=dist+sig |
353 |
> break |
354 |
> fi |
355 |
> fi |
356 |
> done |
357 |
> if [[ ! ${found} ]]; then |
358 |
> nosigfound+=( "${f}" ) |
359 |
> fi |
360 |
> done |
361 |
> |
362 |
> # check if all distfiles are signed |
363 |
> if [[ ${#nosigfound[@]} -gt 0 ]]; then |
364 |
> eerror "The following distfiles lack detached signatures:" |
365 |
> for f in "${nosigfound[@]}"; do |
366 |
> eerror " ${f}" |
367 |
> done |
368 |
> die "Unsigned distfiles found" |
369 |
> fi |
370 |
> |
371 |
> # check if there are no stray signatures |
372 |
> for f in "${signatures[@]}"; do |
373 |
> if ! has "${f%.*}" "${distfiles[@]}"; then |
374 |
> straysigs+=( "${f}" ) |
375 |
> fi |
376 |
> done |
377 |
> if [[ ${#straysigs[@]} -gt 0 ]]; then |
378 |
> eerror "The following signatures do not match any distfiles:" |
379 |
> for f in "${straysigs[@]}"; do |
380 |
> eerror " ${f}" |
381 |
> done |
382 |
> die "Unused signatures found" |
383 |
> fi |
384 |
> |
385 |
> # now perform the verification |
386 |
> for f in "${signatures[@]}"; do |
387 |
> verify-sig_verify_detached \ |
388 |
> "${DISTDIR}/${f%.*}" "${DISTDIR}/${f}" |
389 |
> done |
390 |
> fi |
391 |
> |
392 |
> # finally, unpack the distfiles |
393 |
> default_src_unpack |
394 |
> } |
395 |
> |
396 |
> _VERIFY_SIG_ECLASS=1 |
397 |
> fi |
398 |
|
399 |
-- |
400 |
Best regards, |
401 |
Michał Górny |