Skip to content

Commit 0c12634

Browse files
committed
add gpg verification methods using gpg and gpgv binaries
1 parent 215e38f commit 0c12634

File tree

1 file changed

+101
-1
lines changed

1 file changed

+101
-1
lines changed

src/bmaptool/CLI.py

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import shutil
4040
import io
4141
import pathlib
42+
import subprocess
43+
import re
4244
from typing import NamedTuple
4345
from . import BmapCreate, BmapCopy, BmapHelpers, TransRead
4446

@@ -184,6 +186,79 @@ def fpr2uid(fpr):
184186
]
185187

186188

189+
def verify_bmap_signature_gpgbin(bmap_obj, detached_sig, gpgargv):
190+
with tempfile.TemporaryDirectory(suffix=".bmaptool.gnupg") as td:
191+
if detached_sig:
192+
with open(f"{td}/sig", "wb") as f:
193+
shutil.copyfileobj(detached_sig, f)
194+
gpgargv.append(f"{td}/sig")
195+
with open(f"{td}/bmap", "wb") as f:
196+
shutil.copyfileobj(bmap_obj, f)
197+
gpgargv.append(f"{td}/bmap")
198+
sp = subprocess.Popen(
199+
gpgargv,
200+
stdout=subprocess.PIPE,
201+
stderr=subprocess.PIPE,
202+
)
203+
(output, error) = sp.communicate()
204+
if sp.returncode > 0:
205+
if error.find(b"[GNUPG:] NO_PUBKEY "):
206+
error_out("No matching key found")
207+
error_out("Failed to validate PGP signature")
208+
209+
# regexes are from patatt and b4
210+
short_fpr = None
211+
uid = None
212+
gs_matches = re.search(
213+
rb"^\[GNUPG:] GOODSIG ([0-9A-F]+)\s+(.*)$", error, flags=re.M
214+
)
215+
if gs_matches:
216+
good = True
217+
short_fpr, uid = gs_matches.groups()
218+
vs_matches = re.search(
219+
rb"^\[GNUPG:] VALIDSIG ([0-9A-F]+) (\d{4}-\d{2}-\d{2}) (\d+)",
220+
error,
221+
flags=re.M,
222+
)
223+
if vs_matches:
224+
valid = True
225+
fpr, signdate, signepoch = vs_matches.groups()
226+
if not fpr.endswith(short_fpr):
227+
error_out("good fingerprint does not match valid fingerprint")
228+
if (b': Good signature from "' + uid + b'"') not in error:
229+
log.warning("Unable to find good signature in gpg stderr output")
230+
return output, [
231+
Signature(
232+
good and valid,
233+
fpr.decode(),
234+
uid.decode(),
235+
)
236+
]
237+
238+
239+
def verify_bmap_signature_gpgv(bmap_obj, detached_sig):
240+
return verify_bmap_signature_gpgbin(
241+
bmap_obj, detached_sig, ["gpgv", "--status-fd=2"]
242+
)
243+
244+
245+
def verify_bmap_signature_gpg(bmap_obj, detached_sig):
246+
return verify_bmap_signature_gpgbin(
247+
bmap_obj,
248+
detached_sig,
249+
[
250+
"gpg",
251+
"--batch",
252+
"--no-auto-key-retrieve",
253+
"--no-auto-check-trustdb",
254+
"--verify",
255+
"--output",
256+
"-",
257+
"--status-fd=2",
258+
],
259+
)
260+
261+
187262
def verify_bmap_signature(args, bmap_obj, bmap_path):
188263
"""
189264
Verify GPG signature of the bmap file if it is present. The signature may
@@ -237,7 +312,32 @@ def verify_bmap_signature(args, bmap_obj, bmap_path):
237312

238313
log.info("discovered signature file for bmap '%s'" % detached_sig.name)
239314

240-
plaintext, sigs = verify_bmap_signature_gpgme(bmap_obj, detached_sig)
315+
methods = {
316+
"gpgme": verify_bmap_signature_gpgme,
317+
"gpg": verify_bmap_signature_gpg,
318+
"gpgv": verify_bmap_signature_gpgv,
319+
}
320+
have_method = set()
321+
try:
322+
import gpg
323+
324+
have_method.add("gpgme")
325+
except ImportError:
326+
pass
327+
if shutil.which("gpg") is not None:
328+
have_method.add("gpg")
329+
if shutil.which("gpgv") is not None:
330+
have_method.add("gpgv")
331+
332+
if not have_method:
333+
error_out("Cannot verify GPG signature without GPG")
334+
335+
for method in ["gpgme", "gpgv", "gpg"]:
336+
log.info(f"Trying to verify signature using {method}")
337+
if method not in have_method:
338+
continue
339+
plaintext, sigs = methods[method](bmap_obj, detached_sig)
340+
break
241341
bmap_obj.seek(0)
242342

243343
if not args.no_sig_verify:

0 commit comments

Comments
 (0)