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