commit:     020feffb39cd6c4c68a4a81be6ccd2426b062e8d
Author:     Sheng Yu <syu.os <AT> protonmail <DOT> com>
AuthorDate: Fri Aug 19 20:40:59 2022 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Fri Sep  9 10:15:58 2022 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=020feffb

Add gpkg-sign tool to sign exist GPKG files.

Signed-off-by: Sheng Yu <syu.os <AT> protonmail.com>
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>

 bin/gpkg-sign       | 68 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/portage/gpkg.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 setup.py            |  1 +
 3 files changed, 142 insertions(+)

diff --git a/bin/gpkg-sign b/bin/gpkg-sign
new file mode 100755
index 000000000..57fc6ce98
--- /dev/null
+++ b/bin/gpkg-sign
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# Copyright 1999-2022 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import argparse
+import sys
+
+import portage
+import portage.exception
+
+portage._internal_caller = True
+from portage import gpkg
+
+
+def main(
+    gpkg_file, keep_current_signature=False, allow_unsigned=False, 
skip_signed=False
+):
+    eout = portage.output.EOutput()
+    try:
+        package = gpkg.gpkg(settings=portage.settings, gpkg_file=gpkg_file)
+        if allow_unsigned:
+            package.request_signature = False
+        package._verify_binpkg()
+        if skip_signed and package.signature_exist:
+            eout.einfo(f"{gpkg_file} already signed, skipping.")
+            return
+        package.update_signature(keep_current_signature=keep_current_signature)
+        eout.einfo(f"{gpkg_file} signed.")
+    except portage.exception.FileNotFound:
+        eout.eerror(f"File not found: {gpkg_file}")
+        exit(1)
+    except portage.exception.InvalidBinaryPackageFormat:
+        eout.eerror(f"Invalid binary package format: {gpkg_file}")
+        exit(1)
+    except portage.exception.SignatureException:
+        eout.eerror(f"Signature exception: {gpkg_file}")
+        exit(1)
+
+
+if __name__ == "__main__":
+    usage = "gpkg-sign [options] <gpkg package file>"
+    parser = argparse.ArgumentParser(usage=usage)
+    parser.add_argument(
+        "--keep-current-signature",
+        action="store_true",
+        help="Keep existing signature when updating signature (default: 
false)",
+    )
+    parser.add_argument(
+        "--allow-unsigned",
+        action="store_true",
+        help="Allow signing from unsigned packages when 
binpkg-request-signature is enabled (default: false)",
+    )
+    parser.add_argument(
+        "--skip-signed",
+        action="store_true",
+        help="Skip signing if a package is already signed (default: false)",
+    )
+    options, args = parser.parse_known_args(sys.argv[1:])
+
+    if not args:
+        parser.error("no GPKG oackage file specified")
+
+    main(
+        args[0],
+        keep_current_signature=options.keep_current_signature,
+        allow_unsigned=options.allow_unsigned,
+        skip_signed=options.skip_signed,
+    )

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index 303773616..644ff412b 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -781,6 +781,7 @@ class gpkg:
         self.base_name = base_name
         self.checksums = []
         self.manifest_old = []
+        signature_exist = None
 
         # Compression is the compression algorithm, if set to None will
         # not use compression.
@@ -1118,6 +1119,77 @@ class gpkg:
 
         shutil.move(tmp_gpkg_file_name, self.gpkg_file)
 
+    def update_signature(self, keep_current_signature=False):
+        """
+        Add / update signature in the gpkg file.
+        if keep_current_signature is True, keep the current signature, 
otherwise, re-signing it.
+        """
+        self.create_signature = True
+        self._verify_binpkg()
+        self.checksums = []
+
+        with open(self.gpkg_file, "rb") as container:
+            container_tar_format = self._get_tar_format(container)
+            if container_tar_format is None:
+                raise InvalidBinaryPackageFormat("Cannot identify tar format")
+
+        # container
+        tmp_gpkg_file_name = f"{self.gpkg_file}.{os.getpid()}"
+        with tarfile.TarFile(
+            name=tmp_gpkg_file_name, mode="w", format=container_tar_format
+        ) as container:
+            # gpkg version
+            gpkg_version_file = tarfile.TarInfo(self.gpkg_version)
+            gpkg_version_file.mtime = datetime.utcnow().timestamp()
+            container.addfile(gpkg_version_file)
+
+            # reuse other stuffs
+            with tarfile.open(self.gpkg_file, "r") as container_old:
+                manifest_old = self.manifest_old.copy()
+                file_list_old = [f[1] for f in manifest_old]
+
+                for m in manifest_old:
+                    file_name_old = m[1]
+                    if os.path.basename(file_name_old).endswith(".sig"):
+                        continue
+                    old_data_tarinfo = container_old.getmember(file_name_old)
+                    new_data_tarinfo = copy(old_data_tarinfo)
+
+                    container.addfile(
+                        new_data_tarinfo, 
container_old.extractfile(old_data_tarinfo)
+                    )
+                    self.checksums.append(m)
+
+                    # Check if signature file exists and reuse or create new 
one.
+                    if keep_current_signature and (
+                        file_name_old + ".sig" in file_list_old
+                    ):
+                        old_data_sign_tarinfo = container_old.getmember(
+                            file_name_old + ".sig"
+                        )
+                        new_data_sign_tarinfo = copy(old_data_sign_tarinfo)
+                        container.addfile(
+                            new_data_sign_tarinfo,
+                            container_old.extractfile(old_data_sign_tarinfo),
+                        )
+                        for manifest_sign in manifest_old:
+                            if manifest_sign[1] == file_name_old + ".sig":
+                                self.checksums.append(manifest_sign)
+                                break
+                    else:
+                        checksum_info = checksum_helper(
+                            self.settings, 
gpg_operation=checksum_helper.SIGNING
+                        )
+                        checksum_info.update(
+                            container_old.extractfile(old_data_tarinfo).read()
+                        )
+                        checksum_info.finish()
+                        self._add_signature(checksum_info, new_data_tarinfo, 
container)
+
+            self._add_manifest(container)
+
+        shutil.move(tmp_gpkg_file_name, self.gpkg_file)
+
     def _add_metadata(self, container, metadata, compression_cmd):
         """
         add metadata to container
@@ -1611,6 +1683,7 @@ class gpkg:
 
         # Save current Manifest for other operations.
         self.manifest_old = manifest.copy()
+        self.signature_exist = signature_exist
 
     def _generate_metadata_from_dir(self, metadata_dir):
         """

diff --git a/setup.py b/setup.py
index 8ed507d16..e65963e49 100755
--- a/setup.py
+++ b/setup.py
@@ -66,6 +66,7 @@ x_scripts = {
         "bin/glsa-check",
         "bin/portageq",
         "bin/quickpkg",
+        "bin/gpkg-sign",
     ],
     "sbin": [
         "bin/archive-conf",

Reply via email to