1 |
commit: 020feffb39cd6c4c68a4a81be6ccd2426b062e8d |
2 |
Author: Sheng Yu <syu.os <AT> protonmail <DOT> com> |
3 |
AuthorDate: Fri Aug 19 20:40:59 2022 +0000 |
4 |
Commit: Michał Górny <mgorny <AT> gentoo <DOT> org> |
5 |
CommitDate: Fri Sep 9 10:15:58 2022 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=020feffb |
7 |
|
8 |
Add gpkg-sign tool to sign exist GPKG files. |
9 |
|
10 |
Signed-off-by: Sheng Yu <syu.os <AT> protonmail.com> |
11 |
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org> |
12 |
|
13 |
bin/gpkg-sign | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ |
14 |
lib/portage/gpkg.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
15 |
setup.py | 1 + |
16 |
3 files changed, 142 insertions(+) |
17 |
|
18 |
diff --git a/bin/gpkg-sign b/bin/gpkg-sign |
19 |
new file mode 100755 |
20 |
index 000000000..57fc6ce98 |
21 |
--- /dev/null |
22 |
+++ b/bin/gpkg-sign |
23 |
@@ -0,0 +1,68 @@ |
24 |
+#!/usr/bin/env python |
25 |
+# Copyright 1999-2022 Gentoo Authors |
26 |
+# Distributed under the terms of the GNU General Public License v2 |
27 |
+ |
28 |
+import argparse |
29 |
+import sys |
30 |
+ |
31 |
+import portage |
32 |
+import portage.exception |
33 |
+ |
34 |
+portage._internal_caller = True |
35 |
+from portage import gpkg |
36 |
+ |
37 |
+ |
38 |
+def main( |
39 |
+ gpkg_file, keep_current_signature=False, allow_unsigned=False, skip_signed=False |
40 |
+): |
41 |
+ eout = portage.output.EOutput() |
42 |
+ try: |
43 |
+ package = gpkg.gpkg(settings=portage.settings, gpkg_file=gpkg_file) |
44 |
+ if allow_unsigned: |
45 |
+ package.request_signature = False |
46 |
+ package._verify_binpkg() |
47 |
+ if skip_signed and package.signature_exist: |
48 |
+ eout.einfo(f"{gpkg_file} already signed, skipping.") |
49 |
+ return |
50 |
+ package.update_signature(keep_current_signature=keep_current_signature) |
51 |
+ eout.einfo(f"{gpkg_file} signed.") |
52 |
+ except portage.exception.FileNotFound: |
53 |
+ eout.eerror(f"File not found: {gpkg_file}") |
54 |
+ exit(1) |
55 |
+ except portage.exception.InvalidBinaryPackageFormat: |
56 |
+ eout.eerror(f"Invalid binary package format: {gpkg_file}") |
57 |
+ exit(1) |
58 |
+ except portage.exception.SignatureException: |
59 |
+ eout.eerror(f"Signature exception: {gpkg_file}") |
60 |
+ exit(1) |
61 |
+ |
62 |
+ |
63 |
+if __name__ == "__main__": |
64 |
+ usage = "gpkg-sign [options] <gpkg package file>" |
65 |
+ parser = argparse.ArgumentParser(usage=usage) |
66 |
+ parser.add_argument( |
67 |
+ "--keep-current-signature", |
68 |
+ action="store_true", |
69 |
+ help="Keep existing signature when updating signature (default: false)", |
70 |
+ ) |
71 |
+ parser.add_argument( |
72 |
+ "--allow-unsigned", |
73 |
+ action="store_true", |
74 |
+ help="Allow signing from unsigned packages when binpkg-request-signature is enabled (default: false)", |
75 |
+ ) |
76 |
+ parser.add_argument( |
77 |
+ "--skip-signed", |
78 |
+ action="store_true", |
79 |
+ help="Skip signing if a package is already signed (default: false)", |
80 |
+ ) |
81 |
+ options, args = parser.parse_known_args(sys.argv[1:]) |
82 |
+ |
83 |
+ if not args: |
84 |
+ parser.error("no GPKG oackage file specified") |
85 |
+ |
86 |
+ main( |
87 |
+ args[0], |
88 |
+ keep_current_signature=options.keep_current_signature, |
89 |
+ allow_unsigned=options.allow_unsigned, |
90 |
+ skip_signed=options.skip_signed, |
91 |
+ ) |
92 |
|
93 |
diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py |
94 |
index 303773616..644ff412b 100644 |
95 |
--- a/lib/portage/gpkg.py |
96 |
+++ b/lib/portage/gpkg.py |
97 |
@@ -781,6 +781,7 @@ class gpkg: |
98 |
self.base_name = base_name |
99 |
self.checksums = [] |
100 |
self.manifest_old = [] |
101 |
+ signature_exist = None |
102 |
|
103 |
# Compression is the compression algorithm, if set to None will |
104 |
# not use compression. |
105 |
@@ -1118,6 +1119,77 @@ class gpkg: |
106 |
|
107 |
shutil.move(tmp_gpkg_file_name, self.gpkg_file) |
108 |
|
109 |
+ def update_signature(self, keep_current_signature=False): |
110 |
+ """ |
111 |
+ Add / update signature in the gpkg file. |
112 |
+ if keep_current_signature is True, keep the current signature, otherwise, re-signing it. |
113 |
+ """ |
114 |
+ self.create_signature = True |
115 |
+ self._verify_binpkg() |
116 |
+ self.checksums = [] |
117 |
+ |
118 |
+ with open(self.gpkg_file, "rb") as container: |
119 |
+ container_tar_format = self._get_tar_format(container) |
120 |
+ if container_tar_format is None: |
121 |
+ raise InvalidBinaryPackageFormat("Cannot identify tar format") |
122 |
+ |
123 |
+ # container |
124 |
+ tmp_gpkg_file_name = f"{self.gpkg_file}.{os.getpid()}" |
125 |
+ with tarfile.TarFile( |
126 |
+ name=tmp_gpkg_file_name, mode="w", format=container_tar_format |
127 |
+ ) as container: |
128 |
+ # gpkg version |
129 |
+ gpkg_version_file = tarfile.TarInfo(self.gpkg_version) |
130 |
+ gpkg_version_file.mtime = datetime.utcnow().timestamp() |
131 |
+ container.addfile(gpkg_version_file) |
132 |
+ |
133 |
+ # reuse other stuffs |
134 |
+ with tarfile.open(self.gpkg_file, "r") as container_old: |
135 |
+ manifest_old = self.manifest_old.copy() |
136 |
+ file_list_old = [f[1] for f in manifest_old] |
137 |
+ |
138 |
+ for m in manifest_old: |
139 |
+ file_name_old = m[1] |
140 |
+ if os.path.basename(file_name_old).endswith(".sig"): |
141 |
+ continue |
142 |
+ old_data_tarinfo = container_old.getmember(file_name_old) |
143 |
+ new_data_tarinfo = copy(old_data_tarinfo) |
144 |
+ |
145 |
+ container.addfile( |
146 |
+ new_data_tarinfo, container_old.extractfile(old_data_tarinfo) |
147 |
+ ) |
148 |
+ self.checksums.append(m) |
149 |
+ |
150 |
+ # Check if signature file exists and reuse or create new one. |
151 |
+ if keep_current_signature and ( |
152 |
+ file_name_old + ".sig" in file_list_old |
153 |
+ ): |
154 |
+ old_data_sign_tarinfo = container_old.getmember( |
155 |
+ file_name_old + ".sig" |
156 |
+ ) |
157 |
+ new_data_sign_tarinfo = copy(old_data_sign_tarinfo) |
158 |
+ container.addfile( |
159 |
+ new_data_sign_tarinfo, |
160 |
+ container_old.extractfile(old_data_sign_tarinfo), |
161 |
+ ) |
162 |
+ for manifest_sign in manifest_old: |
163 |
+ if manifest_sign[1] == file_name_old + ".sig": |
164 |
+ self.checksums.append(manifest_sign) |
165 |
+ break |
166 |
+ else: |
167 |
+ checksum_info = checksum_helper( |
168 |
+ self.settings, gpg_operation=checksum_helper.SIGNING |
169 |
+ ) |
170 |
+ checksum_info.update( |
171 |
+ container_old.extractfile(old_data_tarinfo).read() |
172 |
+ ) |
173 |
+ checksum_info.finish() |
174 |
+ self._add_signature(checksum_info, new_data_tarinfo, container) |
175 |
+ |
176 |
+ self._add_manifest(container) |
177 |
+ |
178 |
+ shutil.move(tmp_gpkg_file_name, self.gpkg_file) |
179 |
+ |
180 |
def _add_metadata(self, container, metadata, compression_cmd): |
181 |
""" |
182 |
add metadata to container |
183 |
@@ -1611,6 +1683,7 @@ class gpkg: |
184 |
|
185 |
# Save current Manifest for other operations. |
186 |
self.manifest_old = manifest.copy() |
187 |
+ self.signature_exist = signature_exist |
188 |
|
189 |
def _generate_metadata_from_dir(self, metadata_dir): |
190 |
""" |
191 |
|
192 |
diff --git a/setup.py b/setup.py |
193 |
index 8ed507d16..e65963e49 100755 |
194 |
--- a/setup.py |
195 |
+++ b/setup.py |
196 |
@@ -66,6 +66,7 @@ x_scripts = { |
197 |
"bin/glsa-check", |
198 |
"bin/portageq", |
199 |
"bin/quickpkg", |
200 |
+ "bin/gpkg-sign", |
201 |
], |
202 |
"sbin": [ |
203 |
"bin/archive-conf", |