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 |