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",