commit: 42847dc3ba230bce06ebca836b0bc90ac5521304 Author: Sam James <sam <AT> gentoo <DOT> org> AuthorDate: Mon Jan 5 04:56:50 2026 +0000 Commit: Sam James <sam <AT> gentoo <DOT> org> CommitDate: Mon Jan 5 21:12:01 2026 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=42847dc3
gpkg: provide error summaries if verification fails Do a pass over the error output to see if we can produce a short summary of the problem for the user, then share the raw GnuPG output marked clearly as such below. For example, if /etc/portage/gnupg doesn't exist.. Before: ``` * Binary package is not usable: * gpg: keyblock resource '/etc/portage/gnupg/pubring.kbx': No such file or directory * [GNUPG:] ERROR add_keyblock_resource 33587281 * [GNUPG:] PLAINTEXT 74 0 * [GNUPG:] NEWSIG * gpg: Signature made Thu 09 Oct 2025 15:22:49 BST * gpg: using RSA key 534E4209AB49EEE1C19D96162C44695DB9F6043D * [GNUPG:] ERROR keydb_search 33554445 * [GNUPG:] ERROR keydb_search 33554445 * [GNUPG:] ERRSIG 2C44695DB9F6043D 1 10 01 1760019769 9 534E4209AB49EEE1C19D96162C44695DB9F6043D * [GNUPG:] NO_PUBKEY 2C44695DB9F6043D * gpg: Can't check signature: No public key * [GNUPG:] FAILURE gpg-exit 33554433 * gpg: can't create `/etc/portage/gnupg/random_seed': No such file or directory !!! Invalid binary package: '/var/cache/binhost/gentoo/sys-libs/glibc/glibc-2.41-r6-4.gpkg.tar', GnuPG verification failed ``` After: ``` * Binary package is not usable (verification failed): * Summary: * binpkg signed with at least one unknown key (try running PORTAGE_TRUST_HELPER=getuto?) * * Raw GnuPG output: * gpg: keyblock resource '/etc/portage/gnupg/pubring.kbx': No such file or directory * [GNUPG:] ERROR add_keyblock_resource 33587281 * [GNUPG:] PLAINTEXT 74 0 * [GNUPG:] NEWSIG * gpg: Signature made Thu 09 Oct 2025 15:22:49 BST * gpg: using RSA key 534E4209AB49EEE1C19D96162C44695DB9F6043D * [GNUPG:] ERROR keydb_search 33554445 * [GNUPG:] ERROR keydb_search 33554445 * [GNUPG:] ERRSIG 2C44695DB9F6043D 1 10 01 1760019769 9 534E4209AB49EEE1C19D96162C44695DB9F6043D * [GNUPG:] NO_PUBKEY 2C44695DB9F6043D * gpg: Can't check signature: No public key * [GNUPG:] FAILURE gpg-exit 33554433 * gpg: can't create `/etc/portage/gnupg/random_seed': No such file or directory !!! Invalid binary package: '/var/cache/binhost/gentoo/sys-libs/glibc/glibc-2.41-r6-4.gpkg.tar', GnuPG verification failed ``` This helps to identify the common issues users have: * no keyring at all at /etc/portage/gnupg, or * binpkg isn't signed (local binpkg of their own but verification is enabled globally) Bug: https://bugs.gentoo.org/930730 Bug: https://bugs.gentoo.org/966958 Signed-off-by: Sam James <sam <AT> gentoo.org> lib/portage/gpkg.py | 70 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py index 67a4d39ff3..4f6a05cc84 100644 --- a/lib/portage/gpkg.py +++ b/lib/portage/gpkg.py @@ -1,4 +1,4 @@ -# Copyright 2001-2025 Gentoo Authors +# Copyright 2001-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import tarfile @@ -9,6 +9,7 @@ import subprocess import errno import pwd import grp +import re import shlex import stat import sys @@ -541,6 +542,50 @@ class checksum_helper: def __del__(self): self.finish() + def show_gpg_error(self, operation, gpg_error_lines): + """ + Interpret GnuPG output to give a pretty error message + with a summary if possible. + """ + if operation == checksum_helper.VERIFY: + operation_blurb = "verification failed" + elif operation == checksum_helper.SIGNING: + operation_blurb = "signing failed" + + # Attempt to give a nicer error the sniffing the status output. + error_summaries = [] + portage_trust_helper = self.settings.get("PORTAGE_TRUST_HELPER", "") + + def _match_list(regex: re.Pattern, msgs: list) -> list[re.Match]: + return list(filter(lambda s: re.match(regex, s), msgs)) + + if _match_list(r"^\[GNUPG:\] NODATA", gpg_error_lines): + error_summaries.append("binpkg appears unsigned (missing any signature)") + if _match_list(r"^\[GNUPG:\] NO_PUBKEY", gpg_error_lines): + error_summaries.append( + "binpkg signed with at least one unknown key " + + f"(try running PORTAGE_TRUST_HELPER={portage_trust_helper}?)" + ) + if _match_list(r"^\[GNUPG:\] TRUST_UNDEFINED", gpg_error_lines): + error_summaries.append( + f"binpkg signed with a known key of undefined trust " + + f"(try running PORTAGE_TRUST_HELPER={portage_trust_helper}?)" + ) + + # Don't show any summary if it's ambiguous, in case of + # a malformed signature. + if len(error_summaries) > 1 or not error_summaries: + error_summaries = ["(none available)"] + + out = portage.output.EOutput() + msg = [f"Binary package is not usable ({operation_blurb}):"] + msg.append(" Summary:") + msg.extend("\t" + line for line in error_summaries) + msg.append("") + msg.append(" Raw GnuPG output:") + msg.extend("\t" + line for line in gpg_error_lines) + [out.eerror(line) for line in msg] + def _check_gpg_status(self, gpg_status: bytes) -> None: """ Check GnuPG status log for extra info. @@ -559,16 +604,10 @@ class checksum_helper: trust_signature = True if (not good_signature) or (not trust_signature): - msg = ["Binary package is not usable:"] - msg.extend( - "\t" + line - for line in self.gpg_result.decode( - "UTF-8", errors="replace" - ).splitlines() - ) - out = portage.output.EOutput() - [out.eerror(line) for line in msg] - + gpg_error_lines = self.gpg_result.decode( + "UTF-8", errors="replace" + ).splitlines() + self.show_gpg_error(checksum_helper.VERIFY, gpg_error_lines) raise InvalidSignature("GnuPG verification failed") def update(self, data): @@ -611,17 +650,12 @@ class checksum_helper: gpg_error_lines = self.gpg_result.decode( "UTF-8", errors="replace" ).splitlines() - out = portage.output.EOutput() if self.gpg_operation == checksum_helper.SIGNING: - msg = ["Binary package is not usable (signing failed):"] - msg.extend("\t" + line for line in gpg_error_lines) - [out.eerror(line) for line in msg] + self.show_gpg_error(checksum_helper.SIGNING, gpg_error_lines) raise GPGException("GnuPG signing failed") elif self.gpg_operation == checksum_helper.VERIFY: - msg = ["Binary package is not usable (verification failed):"] - msg.extend("\t" + line for line in gpg_error_lines) - [out.eerror(line) for line in msg] + self.show_gpg_error(checksum_helper.VERIFY, gpg_error_lines) raise InvalidSignature("GnuPG verification failed")
