commit:     4ccea757a4a7f7153fbc1769a9019978d1a9fafc
Author:     Sam James <sam <AT> gentoo <DOT> org>
AuthorDate: Sat Jan  3 04:46:51 2026 +0000
Commit:     Andreas K. Hüttel <dilfridge <AT> gentoo <DOT> org>
CommitDate: Tue Jan  6 15:31:29 2026 +0000
URL:        https://gitweb.gentoo.org/proj/catalyst.git/commit/?id=4ccea757

Implement OpenPGP verification for git repos

Introduces `repo_openpgp_key_path` for config which defaults to
`/usr/share/openpgp-keys/gentoo-release.asc` which can verify the
stable repo. Uses app-portage/gemato.

Fail noisily if gemato isn't installed and repo_openpgp_key_path is
either set or is the default, as we don't want people to accidentally
have an unverified repo. They can set it to blank instead if they wish.

Bug: https://bugs.gentoo.org/966073
Signed-off-by: Sam James <sam <AT> gentoo.org>
Signed-off-by: Andreas K. Hüttel <dilfridge <AT> gentoo.org>

 catalyst/defaults.py         |  2 ++
 catalyst/targets/snapshot.py | 36 ++++++++++++++++++++++++++++++++++++
 doc/catalyst-config.5.txt    |  4 ++++
 3 files changed, 42 insertions(+)

diff --git a/catalyst/defaults.py b/catalyst/defaults.py
index f4d48fef..f47fcf7a 100644
--- a/catalyst/defaults.py
+++ b/catalyst/defaults.py
@@ -21,6 +21,7 @@ valid_config_file_values = frozenset([
     "options",
     "port_logdir",
     "repo_basedir",
+    "repo_openpgp_key_path",
     "repo_name",
     "repos_storedir",
     "sharedir",
@@ -51,6 +52,7 @@ confdefaults = {
     "pkgdir": "/var/cache/binpkgs",
     "port_tmpdir": "/var/tmp/portage",
     "repo_basedir": "/var/db/repos",
+    "repo_openpgp_key_path": "/usr/share/openpgp-keys/gentoo-release.asc",
     "repo_name": "gentoo",
     "repos_storedir": "%(storedir)s/repos",
     "sharedir": "/usr/share/catalyst",

diff --git a/catalyst/targets/snapshot.py b/catalyst/targets/snapshot.py
index ef68765d..8d4145fd 100644
--- a/catalyst/targets/snapshot.py
+++ b/catalyst/targets/snapshot.py
@@ -55,12 +55,45 @@ class snapshot(TargetBase):
                  repouri, self.gitdir],
             ]
 
+
+        env = os.environ.copy()
+        pgp_keyring = self.settings["repo_openpgp_key_path"]
+        if pgp_keyring:
+            try:
+                import gemato.openpgp
+            except ImportError as e:
+                 raise CatalystError(
+                     f"gemato could not be imported but repo_openpgp_key_path 
was non-empty."
+                 )
+
+            pgp_path = Path(pgp_keyring)
+            if not pgp_path.exists() or not pgp_path.is_file():
+                raise CatalystError(
+                    f"OpenPGP keyring at repo_openpgp_key_path={pgp_path} does 
not exist. Is sec-keys/openpgp-keys-gentoo-release installed?"
+                )
+
+            git_cmds.append(self.git, '-C', self.gitdir, 'verify-commit', 
'HEAD')
+
+            openpgp_env = gemato.openpgp.OpenPGPEnvironment
+            try:
+                with open(pgp_path, "rb") as f:
+                    openpgp_env.import_key(f)
+                    openpgp_env.refresh_keys()
+            except (GematoException, asyncio.TimeoutError) as e:
+                raise CatalystError(
+                    f"OpenPGP verification via gemato failed: {e}"
+                )
+                openpgp_env.close()
+
+            env["GNUPGHOME"] = openpgp_env.home
+
         try:
             for cmd in git_cmds:
                 log.notice('>>> ' + ' '.join(cmd))
                 subprocess.run(cmd,
                                capture_output=True,
                                check=True,
+                               env=env,
                                encoding='utf-8',
                                close_fds=False)
 
@@ -75,6 +108,9 @@ class snapshot(TargetBase):
             raise CatalystError(f'{e.cmd} failed with return code'
                                 f'{e.returncode}\n'
                                 f'{e.output}\n') from e
+        finally:
+            if pgp_keyring:
+                openpgp_env.close()
 
     def run(self):
         if self.settings['snapshot_treeish'] == 'stable':

diff --git a/doc/catalyst-config.5.txt b/doc/catalyst-config.5.txt
index ca9335d6..7dd44cd8 100644
--- a/doc/catalyst-config.5.txt
+++ b/doc/catalyst-config.5.txt
@@ -124,6 +124,10 @@ The name of the main repository (e.g. gentoo). The git 
repository at
 `${repos_storedir}/${repo_name}.git` will be used to produce the portdir sqfs
 snapshot.
 
+*repo_openpgp_key_path*::
+OpenPGP keyring for verifying commits in the git repository. Defaults to
+`/usr/share/openpgp-keys/gentoo-release.asc`.
+
 *target_distdir*::
 Defines the location of the local source file repository in the
 target.  This will be written to the target's make.conf if it is not

Reply via email to