Merge ~lgp171188/launchpad:update-publisher-multi-sign-archive-when-multiple-keys-available into launchpad:master

Proposed by Guruprasad
Status: Merged
Approved by: Guruprasad
Approved revision: d340954deb191a7e6ee34bcad2159f3aa5c54e34
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~lgp171188/launchpad:update-publisher-multi-sign-archive-when-multiple-keys-available
Merge into: launchpad:master
Diff against target: 681 lines (+213/-74)
10 files modified
lib/lp/archivepublisher/archivegpgsigningkey.py (+26/-12)
lib/lp/archivepublisher/signing.py (+6/-4)
lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py (+61/-4)
lib/lp/archivepublisher/tests/test_signing.py (+30/-30)
lib/lp/services/signing/interfaces/signingkey.py (+20/-10)
lib/lp/services/signing/interfaces/signingserviceclient.py (+6/-5)
lib/lp/services/signing/model/signingkey.py (+33/-3)
lib/lp/services/signing/proxy.py (+21/-2)
lib/lp/services/signing/tests/test_proxy.py (+3/-1)
lib/lp/services/signing/tests/test_signingkey.py (+7/-3)
Reviewer Review Type Date Requested Status
Simone Pelosi Approve
Review via email: mp+464692@code.launchpad.net

Commit message

Sign the archive with all its OpenPGP signing keys

If an archive has more than one OpenPGP signing key, sign the archive's
metadata files with all the available keys. For this, make signing an
operation on the signing key set and require passing a list of keys
to all signing operations.

To post a comment you must log in.
Revision history for this message
Simone Pelosi (pelpsi) wrote :

Great work! Looks good to me! I just left some comments related to the docstrings

review: Approve
Revision history for this message
Guruprasad (lgp171188) wrote :

> Great work! Looks good to me! I just left some comments related to the
> docstrings

Thanks for the review, Simone! I have addressed them and pushed a new revision. Will merge this MP after the lpci checks pass.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/archivepublisher/archivegpgsigningkey.py b/lib/lp/archivepublisher/archivegpgsigningkey.py
index f24322d..34267a4 100644
--- a/lib/lp/archivepublisher/archivegpgsigningkey.py
+++ b/lib/lp/archivepublisher/archivegpgsigningkey.py
@@ -72,16 +72,29 @@ class SignableArchive:
72 )72 )
7373
74 @cachedproperty74 @cachedproperty
75 def _signing_key(self):75 def _signing_keys(self):
76 """This archive's signing key on the signing service, if any."""76 """This archive's signing keys on the signing service, if any."""
77 if not getFeatureFlag(PUBLISHER_GPG_USES_SIGNING_SERVICE):77 if not getFeatureFlag(PUBLISHER_GPG_USES_SIGNING_SERVICE):
78 return None78 return []
79 elif self.archive.signing_key_fingerprint is not None:79
80 return getUtility(ISigningKeySet).get(80 signing_keys = []
81 SigningKeyType.OPENPGP, self.archive.signing_key_fingerprint81 signing_keys.extend(
82 getUtility(IArchiveSigningKeySet).getOpenPGPSigningKeysForArchive(
83 self.archive,
82 )84 )
83 else:85 )
84 return None86 if self.archive.signing_key_fingerprint is not None:
87 if not any(
88 key.fingerprint == self.archive.signing_key_fingerprint
89 for key in signing_keys
90 ):
91 signing_key = getUtility(ISigningKeySet).get(
92 SigningKeyType.OPENPGP,
93 self.archive.signing_key_fingerprint,
94 )
95 if signing_key:
96 signing_keys.append(signing_key)
97 return signing_keys
8598
86 @cachedproperty99 @cachedproperty
87 def _secret_key(self):100 def _secret_key(self):
@@ -121,12 +134,13 @@ class SignableArchive:
121 raise ValueError("Invalid signature mode for GPG: %s" % mode)134 raise ValueError("Invalid signature mode for GPG: %s" % mode)
122 signed = False135 signed = False
123136
124 if self._signing_key is not None or self._secret_key is not None:137 if self._signing_keys or self._secret_key is not None:
125 with open(input_path, "rb") as input_file:138 with open(input_path, "rb") as input_file:
126 input_content = input_file.read()139 input_content = input_file.read()
127 if self._signing_key is not None:140 if self._signing_keys:
128 try:141 try:
129 signature = self._signing_key.sign(142 signature = getUtility(ISigningKeySet).sign(
143 self._signing_keys,
130 input_content,144 input_content,
131 os.path.basename(input_path),145 os.path.basename(input_path),
132 mode=mode,146 mode=mode,
@@ -138,7 +152,7 @@ class SignableArchive:
138 "Failed to sign archive using signing "152 "Failed to sign archive using signing "
139 "service; falling back to local key"153 "service; falling back to local key"
140 )154 )
141 get_property_cache(self)._signing_key = None155 get_property_cache(self)._signing_keys = None
142 if not signed and self._secret_key is not None:156 if not signed and self._secret_key is not None:
143 signature = getUtility(IGPGHandler).signContent(157 signature = getUtility(IGPGHandler).signContent(
144 input_content,158 input_content,
diff --git a/lib/lp/archivepublisher/signing.py b/lib/lp/archivepublisher/signing.py
index 9938a95..899ddf1 100644
--- a/lib/lp/archivepublisher/signing.py
+++ b/lib/lp/archivepublisher/signing.py
@@ -32,7 +32,10 @@ from lp.registry.interfaces.distroseries import IDistroSeriesSet
32from lp.services.features import getFeatureFlag32from lp.services.features import getFeatureFlag
33from lp.services.osutils import remove_if_exists33from lp.services.osutils import remove_if_exists
34from lp.services.signing.enums import SigningKeyType34from lp.services.signing.enums import SigningKeyType
35from lp.services.signing.interfaces.signingkey import IArchiveSigningKeySet35from lp.services.signing.interfaces.signingkey import (
36 IArchiveSigningKeySet,
37 ISigningKeySet,
38)
36from lp.soyuz.interfaces.queue import CustomUploadError39from lp.soyuz.interfaces.queue import CustomUploadError
3740
38PUBLISHER_USES_SIGNING_SERVICE = "archivepublisher.signing_service.enabled"41PUBLISHER_USES_SIGNING_SERVICE = "archivepublisher.signing_service.enabled"
@@ -398,10 +401,9 @@ class SigningUpload(CustomUpload):
398401
399 with open(filename, "rb") as fd:402 with open(filename, "rb") as fd:
400 content = fd.read()403 content = fd.read()
401
402 try:404 try:
403 signed_content = signing_key.sign(405 signed_content = getUtility(ISigningKeySet).sign(
404 content, message_name=os.path.basename(filename)406 [signing_key], content, message_name=os.path.basename(filename)
405 )407 )
406 except Exception as e:408 except Exception as e:
407 if self.logger:409 if self.logger:
diff --git a/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py b/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py
index e51077a..ff2ce27 100644
--- a/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py
+++ b/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py
@@ -38,7 +38,10 @@ from lp.services.gpg.tests.test_gpghandler import FakeGenerateKey
38from lp.services.log.logger import BufferLogger38from lp.services.log.logger import BufferLogger
39from lp.services.osutils import write_file39from lp.services.osutils import write_file
40from lp.services.signing.enums import SigningKeyType, SigningMode40from lp.services.signing.enums import SigningKeyType, SigningMode
41from lp.services.signing.interfaces.signingkey import ISigningKeySet41from lp.services.signing.interfaces.signingkey import (
42 IArchiveSigningKeySet,
43 ISigningKeySet,
44)
42from lp.services.signing.tests.helpers import SigningServiceClientFixture45from lp.services.signing.tests.helpers import SigningServiceClientFixture
43from lp.services.twistedsupport.testing import TReqFixture46from lp.services.twistedsupport.testing import TReqFixture
44from lp.services.twistedsupport.treq import check_status47from lp.services.twistedsupport.treq import check_status
@@ -146,14 +149,14 @@ class TestSignableArchiveWithSigningKey(TestCaseWithFactory):
146 [149 [
147 mock.call(150 mock.call(
148 SigningKeyType.OPENPGP,151 SigningKeyType.OPENPGP,
149 self.archive.signing_key_fingerprint,152 [self.archive.signing_key_fingerprint],
150 "Release",153 "Release",
151 b"Release contents",154 b"Release contents",
152 SigningMode.DETACHED,155 SigningMode.DETACHED,
153 ),156 ),
154 mock.call(157 mock.call(
155 SigningKeyType.OPENPGP,158 SigningKeyType.OPENPGP,
156 self.archive.signing_key_fingerprint,159 [self.archive.signing_key_fingerprint],
157 "Release",160 "Release",
158 b"Release contents",161 b"Release contents",
159 SigningMode.CLEAR,162 SigningMode.CLEAR,
@@ -169,6 +172,60 @@ class TestSignableArchiveWithSigningKey(TestCaseWithFactory):
169 FileContains("signed with key_type=OPENPGP mode=CLEAR"),172 FileContains("signed with key_type=OPENPGP mode=CLEAR"),
170 )173 )
171174
175 def test_signRepository_uses_all_OpenPGPG_keys_of_an_archive(self):
176 self.useFixture(
177 FeatureFixture({PUBLISHER_GPG_USES_SIGNING_SERVICE: "on"})
178 )
179 current_key = self.factory.makeSigningKey(
180 key_type=SigningKeyType.OPENPGP,
181 fingerprint=self.archive.signing_key_fingerprint,
182 )
183 new_key = self.factory.makeSigningKey(
184 key_type=SigningKeyType.OPENPGP,
185 # The fingerprint has to be 30 characters long to pass validation.
186 fingerprint="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234",
187 )
188 self.archive.signing_key_fingerprint = current_key.fingerprint
189 getUtility(IArchiveSigningKeySet).create(
190 self.archive, None, current_key
191 )
192 getUtility(IArchiveSigningKeySet).create(self.archive, None, new_key)
193 signing_service_client = self.useFixture(
194 SigningServiceClientFixture(self.factory)
195 )
196 suite_dir = os.path.join(
197 self.archive_root,
198 "dists",
199 self.suite,
200 )
201 release_path = os.path.join(suite_dir, "Release")
202 write_file(release_path, b"Release contents")
203 logger = BufferLogger()
204 signer = ISignableArchive(self.archive)
205 self.assertTrue(signer.can_sign)
206 self.assertContentEqual(
207 ["Release.gpg", "InRelease"],
208 signer.signRepository(self.suite, log=logger),
209 )
210 signing_service_client.sign.assert_has_calls(
211 [
212 mock.call(
213 SigningKeyType.OPENPGP,
214 [current_key.fingerprint, new_key.fingerprint],
215 "Release",
216 b"Release contents",
217 SigningMode.DETACHED,
218 ),
219 mock.call(
220 SigningKeyType.OPENPGP,
221 [current_key.fingerprint, new_key.fingerprint],
222 "Release",
223 b"Release contents",
224 SigningMode.CLEAR,
225 ),
226 ]
227 )
228
172 def test_signRepository_falls_back_from_signing_service(self):229 def test_signRepository_falls_back_from_signing_service(self):
173 # If the signing service fails to sign a file, we fall back to230 # If the signing service fails to sign a file, we fall back to
174 # making local signatures if possible.231 # making local signatures if possible.
@@ -202,7 +259,7 @@ class TestSignableArchiveWithSigningKey(TestCaseWithFactory):
202 )259 )
203 signing_service_client.sign.assert_called_once_with(260 signing_service_client.sign.assert_called_once_with(
204 SigningKeyType.OPENPGP,261 SigningKeyType.OPENPGP,
205 self.archive.signing_key_fingerprint,262 [self.archive.signing_key_fingerprint],
206 "Release",263 "Release",
207 b"Release contents",264 b"Release contents",
208 SigningMode.DETACHED,265 SigningMode.DETACHED,
diff --git a/lib/lp/archivepublisher/tests/test_signing.py b/lib/lp/archivepublisher/tests/test_signing.py
index c352f3d..91e40cc 100644
--- a/lib/lp/archivepublisher/tests/test_signing.py
+++ b/lib/lp/archivepublisher/tests/test_signing.py
@@ -1955,49 +1955,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
1955 [1955 [
1956 call(1956 call(
1957 SigningKeyType.UEFI,1957 SigningKeyType.UEFI,
1958 keys[SigningKeyType.UEFI].fingerprint,1958 [keys[SigningKeyType.UEFI].fingerprint],
1959 "empty.efi",1959 "empty.efi",
1960 b"a",1960 b"a",
1961 SigningMode.ATTACHED,1961 SigningMode.ATTACHED,
1962 ),1962 ),
1963 call(1963 call(
1964 SigningKeyType.KMOD,1964 SigningKeyType.KMOD,
1965 keys[SigningKeyType.KMOD].fingerprint,1965 [keys[SigningKeyType.KMOD].fingerprint],
1966 "empty.ko",1966 "empty.ko",
1967 b"b",1967 b"b",
1968 SigningMode.DETACHED,1968 SigningMode.DETACHED,
1969 ),1969 ),
1970 call(1970 call(
1971 SigningKeyType.OPAL,1971 SigningKeyType.OPAL,
1972 keys[SigningKeyType.OPAL].fingerprint,1972 [keys[SigningKeyType.OPAL].fingerprint],
1973 "empty.opal",1973 "empty.opal",
1974 b"c",1974 b"c",
1975 SigningMode.DETACHED,1975 SigningMode.DETACHED,
1976 ),1976 ),
1977 call(1977 call(
1978 SigningKeyType.SIPL,1978 SigningKeyType.SIPL,
1979 keys[SigningKeyType.SIPL].fingerprint,1979 [keys[SigningKeyType.SIPL].fingerprint],
1980 "empty.sipl",1980 "empty.sipl",
1981 b"d",1981 b"d",
1982 SigningMode.DETACHED,1982 SigningMode.DETACHED,
1983 ),1983 ),
1984 call(1984 call(
1985 SigningKeyType.FIT,1985 SigningKeyType.FIT,
1986 keys[SigningKeyType.FIT].fingerprint,1986 [keys[SigningKeyType.FIT].fingerprint],
1987 "empty.fit",1987 "empty.fit",
1988 b"e",1988 b"e",
1989 SigningMode.ATTACHED,1989 SigningMode.ATTACHED,
1990 ),1990 ),
1991 call(1991 call(
1992 SigningKeyType.CV2_KERNEL,1992 SigningKeyType.CV2_KERNEL,
1993 keys[SigningKeyType.CV2_KERNEL].fingerprint,1993 [keys[SigningKeyType.CV2_KERNEL].fingerprint],
1994 "empty.cv2-kernel",1994 "empty.cv2-kernel",
1995 b"f",1995 b"f",
1996 SigningMode.DETACHED,1996 SigningMode.DETACHED,
1997 ),1997 ),
1998 call(1998 call(
1999 SigningKeyType.ANDROID_KERNEL,1999 SigningKeyType.ANDROID_KERNEL,
2000 keys[SigningKeyType.ANDROID_KERNEL].fingerprint,2000 [keys[SigningKeyType.ANDROID_KERNEL].fingerprint],
2001 "empty.android-kernel",2001 "empty.android-kernel",
2002 b"g",2002 b"g",
2003 SigningMode.DETACHED,2003 SigningMode.DETACHED,
@@ -2051,49 +2051,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
2051 [2051 [
2052 call(2052 call(
2053 SigningKeyType.UEFI,2053 SigningKeyType.UEFI,
2054 keys[SigningKeyType.UEFI].fingerprint,2054 [keys[SigningKeyType.UEFI].fingerprint],
2055 "empty.efi",2055 "empty.efi",
2056 b"a",2056 b"a",
2057 SigningMode.ATTACHED,2057 SigningMode.ATTACHED,
2058 ),2058 ),
2059 call(2059 call(
2060 SigningKeyType.KMOD,2060 SigningKeyType.KMOD,
2061 keys[SigningKeyType.KMOD].fingerprint,2061 [keys[SigningKeyType.KMOD].fingerprint],
2062 "empty.ko",2062 "empty.ko",
2063 b"b",2063 b"b",
2064 SigningMode.DETACHED,2064 SigningMode.DETACHED,
2065 ),2065 ),
2066 call(2066 call(
2067 SigningKeyType.OPAL,2067 SigningKeyType.OPAL,
2068 keys[SigningKeyType.OPAL].fingerprint,2068 [keys[SigningKeyType.OPAL].fingerprint],
2069 "empty.opal",2069 "empty.opal",
2070 b"c",2070 b"c",
2071 SigningMode.DETACHED,2071 SigningMode.DETACHED,
2072 ),2072 ),
2073 call(2073 call(
2074 SigningKeyType.SIPL,2074 SigningKeyType.SIPL,
2075 keys[SigningKeyType.SIPL].fingerprint,2075 [keys[SigningKeyType.SIPL].fingerprint],
2076 "empty.sipl",2076 "empty.sipl",
2077 b"d",2077 b"d",
2078 SigningMode.DETACHED,2078 SigningMode.DETACHED,
2079 ),2079 ),
2080 call(2080 call(
2081 SigningKeyType.FIT,2081 SigningKeyType.FIT,
2082 keys[SigningKeyType.FIT].fingerprint,2082 [keys[SigningKeyType.FIT].fingerprint],
2083 "empty.fit",2083 "empty.fit",
2084 b"e",2084 b"e",
2085 SigningMode.ATTACHED,2085 SigningMode.ATTACHED,
2086 ),2086 ),
2087 call(2087 call(
2088 SigningKeyType.CV2_KERNEL,2088 SigningKeyType.CV2_KERNEL,
2089 keys[SigningKeyType.CV2_KERNEL].fingerprint,2089 [keys[SigningKeyType.CV2_KERNEL].fingerprint],
2090 "empty.cv2-kernel",2090 "empty.cv2-kernel",
2091 b"f",2091 b"f",
2092 SigningMode.DETACHED,2092 SigningMode.DETACHED,
2093 ),2093 ),
2094 call(2094 call(
2095 SigningKeyType.ANDROID_KERNEL,2095 SigningKeyType.ANDROID_KERNEL,
2096 keys[SigningKeyType.ANDROID_KERNEL].fingerprint,2096 [keys[SigningKeyType.ANDROID_KERNEL].fingerprint],
2097 "empty.android-kernel",2097 "empty.android-kernel",
2098 b"g",2098 b"g",
2099 SigningMode.DETACHED,2099 SigningMode.DETACHED,
@@ -2158,49 +2158,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
2158 [2158 [
2159 call(2159 call(
2160 SigningKeyType.UEFI,2160 SigningKeyType.UEFI,
2161 keys[SigningKeyType.UEFI].fingerprint,2161 [keys[SigningKeyType.UEFI].fingerprint],
2162 "empty.efi",2162 "empty.efi",
2163 b"a",2163 b"a",
2164 SigningMode.ATTACHED,2164 SigningMode.ATTACHED,
2165 ),2165 ),
2166 call(2166 call(
2167 SigningKeyType.KMOD,2167 SigningKeyType.KMOD,
2168 keys[SigningKeyType.KMOD].fingerprint,2168 [keys[SigningKeyType.KMOD].fingerprint],
2169 "empty.ko",2169 "empty.ko",
2170 b"b",2170 b"b",
2171 SigningMode.DETACHED,2171 SigningMode.DETACHED,
2172 ),2172 ),
2173 call(2173 call(
2174 SigningKeyType.OPAL,2174 SigningKeyType.OPAL,
2175 keys[SigningKeyType.OPAL].fingerprint,2175 [keys[SigningKeyType.OPAL].fingerprint],
2176 "empty.opal",2176 "empty.opal",
2177 b"c",2177 b"c",
2178 SigningMode.DETACHED,2178 SigningMode.DETACHED,
2179 ),2179 ),
2180 call(2180 call(
2181 SigningKeyType.SIPL,2181 SigningKeyType.SIPL,
2182 keys[SigningKeyType.SIPL].fingerprint,2182 [keys[SigningKeyType.SIPL].fingerprint],
2183 "empty.sipl",2183 "empty.sipl",
2184 b"d",2184 b"d",
2185 SigningMode.DETACHED,2185 SigningMode.DETACHED,
2186 ),2186 ),
2187 call(2187 call(
2188 SigningKeyType.FIT,2188 SigningKeyType.FIT,
2189 keys[SigningKeyType.FIT].fingerprint,2189 [keys[SigningKeyType.FIT].fingerprint],
2190 "empty.fit",2190 "empty.fit",
2191 b"e",2191 b"e",
2192 SigningMode.ATTACHED,2192 SigningMode.ATTACHED,
2193 ),2193 ),
2194 call(2194 call(
2195 SigningKeyType.CV2_KERNEL,2195 SigningKeyType.CV2_KERNEL,
2196 keys[SigningKeyType.CV2_KERNEL].fingerprint,2196 [keys[SigningKeyType.CV2_KERNEL].fingerprint],
2197 "empty.cv2-kernel",2197 "empty.cv2-kernel",
2198 b"f",2198 b"f",
2199 SigningMode.DETACHED,2199 SigningMode.DETACHED,
2200 ),2200 ),
2201 call(2201 call(
2202 SigningKeyType.ANDROID_KERNEL,2202 SigningKeyType.ANDROID_KERNEL,
2203 keys[SigningKeyType.ANDROID_KERNEL].fingerprint,2203 [keys[SigningKeyType.ANDROID_KERNEL].fingerprint],
2204 "empty.android-kernel",2204 "empty.android-kernel",
2205 b"g",2205 b"g",
2206 SigningMode.DETACHED,2206 SigningMode.DETACHED,
@@ -2323,14 +2323,14 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
2323 [2323 [
2324 call(2324 call(
2325 SigningKeyType.KMOD,2325 SigningKeyType.KMOD,
2326 keys[SigningKeyType.KMOD].fingerprint,2326 [keys[SigningKeyType.KMOD].fingerprint],
2327 "empty.ko",2327 "empty.ko",
2328 b"some data for 1.0/empty.ko",2328 b"some data for 1.0/empty.ko",
2329 SigningMode.DETACHED,2329 SigningMode.DETACHED,
2330 ),2330 ),
2331 call(2331 call(
2332 SigningKeyType.OPAL,2332 SigningKeyType.OPAL,
2333 keys[SigningKeyType.OPAL].fingerprint,2333 [keys[SigningKeyType.OPAL].fingerprint],
2334 "empty.opal",2334 "empty.opal",
2335 b"some data for 1.0/empty.opal",2335 b"some data for 1.0/empty.opal",
2336 SigningMode.DETACHED,2336 SigningMode.DETACHED,
@@ -2410,49 +2410,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
2410 [2410 [
2411 call(2411 call(
2412 SigningKeyType.UEFI,2412 SigningKeyType.UEFI,
2413 fingerprints[SigningKeyType.UEFI],2413 [fingerprints[SigningKeyType.UEFI]],
2414 "empty.efi",2414 "empty.efi",
2415 b"data - 1.0/empty.efi",2415 b"data - 1.0/empty.efi",
2416 SigningMode.ATTACHED,2416 SigningMode.ATTACHED,
2417 ),2417 ),
2418 call(2418 call(
2419 SigningKeyType.KMOD,2419 SigningKeyType.KMOD,
2420 fingerprints[SigningKeyType.KMOD],2420 [fingerprints[SigningKeyType.KMOD]],
2421 "empty.ko",2421 "empty.ko",
2422 b"data - 1.0/empty.ko",2422 b"data - 1.0/empty.ko",
2423 SigningMode.DETACHED,2423 SigningMode.DETACHED,
2424 ),2424 ),
2425 call(2425 call(
2426 SigningKeyType.OPAL,2426 SigningKeyType.OPAL,
2427 fingerprints[SigningKeyType.OPAL],2427 [fingerprints[SigningKeyType.OPAL]],
2428 "empty.opal",2428 "empty.opal",
2429 b"data - 1.0/empty.opal",2429 b"data - 1.0/empty.opal",
2430 SigningMode.DETACHED,2430 SigningMode.DETACHED,
2431 ),2431 ),
2432 call(2432 call(
2433 SigningKeyType.SIPL,2433 SigningKeyType.SIPL,
2434 fingerprints[SigningKeyType.SIPL],2434 [fingerprints[SigningKeyType.SIPL]],
2435 "empty.sipl",2435 "empty.sipl",
2436 b"data - 1.0/empty.sipl",2436 b"data - 1.0/empty.sipl",
2437 SigningMode.DETACHED,2437 SigningMode.DETACHED,
2438 ),2438 ),
2439 call(2439 call(
2440 SigningKeyType.FIT,2440 SigningKeyType.FIT,
2441 fingerprints[SigningKeyType.FIT],2441 [fingerprints[SigningKeyType.FIT]],
2442 "empty.fit",2442 "empty.fit",
2443 b"data - 1.0/empty.fit",2443 b"data - 1.0/empty.fit",
2444 SigningMode.ATTACHED,2444 SigningMode.ATTACHED,
2445 ),2445 ),
2446 call(2446 call(
2447 SigningKeyType.CV2_KERNEL,2447 SigningKeyType.CV2_KERNEL,
2448 fingerprints[SigningKeyType.CV2_KERNEL],2448 [fingerprints[SigningKeyType.CV2_KERNEL]],
2449 "empty.cv2-kernel",2449 "empty.cv2-kernel",
2450 b"data - 1.0/empty.cv2-kernel",2450 b"data - 1.0/empty.cv2-kernel",
2451 SigningMode.DETACHED,2451 SigningMode.DETACHED,
2452 ),2452 ),
2453 call(2453 call(
2454 SigningKeyType.ANDROID_KERNEL,2454 SigningKeyType.ANDROID_KERNEL,
2455 fingerprints[SigningKeyType.ANDROID_KERNEL],2455 [fingerprints[SigningKeyType.ANDROID_KERNEL]],
2456 "empty.android-kernel",2456 "empty.android-kernel",
2457 b"data - 1.0/empty.android-kernel",2457 b"data - 1.0/empty.android-kernel",
2458 SigningMode.DETACHED,2458 SigningMode.DETACHED,
diff --git a/lib/lp/services/signing/interfaces/signingkey.py b/lib/lp/services/signing/interfaces/signingkey.py
index a99e018..b28d703 100644
--- a/lib/lp/services/signing/interfaces/signingkey.py
+++ b/lib/lp/services/signing/interfaces/signingkey.py
@@ -44,16 +44,6 @@ class ISigningKey(Interface):
44 title=_("When this key was created"), required=True, readonly=True44 title=_("When this key was created"), required=True, readonly=True
45 )45 )
4646
47 def sign(message, message_name, mode=None):
48 """Sign the given message using this key
49
50 :param message: The message to be signed.
51 :param message_name: A name for the message being signed.
52 :param mode: A `SigningMode` specifying how the message is to be
53 signed. Defaults to `SigningMode.ATTACHED` for UEFI and FIT
54 keys, and `SigningMode.DETACHED` for other key types.
55 """
56
57 def addAuthorization(client_name):47 def addAuthorization(client_name):
58 """Authorize another client to use this key.48 """Authorize another client to use this key.
5949
@@ -101,6 +91,19 @@ class ISigningKeySet(Interface):
101 key at lp-signing91 key at lp-signing
102 """92 """
10393
94 def sign(signing_keys, message, message_name, mode=None):
95 """Sign the given message using the given keys
96
97 :param signing_keys: A list of one or more signing keys to sign
98 the given message with. If more than one signing key is provided,
99 all signing keys must be of the same type.
100 :param message: The message to be signed.
101 :param message_name: A name for the message being signed.
102 :param mode: A `SigningMode` specifying how the message is to be
103 signed. Defaults to `SigningMode.ATTACHED` for UEFI and FIT
104 keys, and `SigningMode.DETACHED` for other key types.
105 """
106
104107
105class IArchiveSigningKey(Interface):108class IArchiveSigningKey(Interface):
106 """Which signing key should be used by a specific archive"""109 """Which signing key should be used by a specific archive"""
@@ -162,6 +165,13 @@ class IArchiveSigningKeySet(Interface):
162 :return: The most suitable key, or None.165 :return: The most suitable key, or None.
163 """166 """
164167
168 def getOpenPGPSigningKeysForArchive(archive):
169 """Find and return the OpenPGP signing keys for the given archive.
170
171 :param archive: The archive to get the OpenPGP signing keys for.
172 :return: A list of matching signing keys or an empty list.
173 """
174
165 def getByArchiveAndFingerprint(archive, fingerprint):175 def getByArchiveAndFingerprint(archive, fingerprint):
166 """Get ArchiveSigningKey by archive and fingerprint.176 """Get ArchiveSigningKey by archive and fingerprint.
167177
diff --git a/lib/lp/services/signing/interfaces/signingserviceclient.py b/lib/lp/services/signing/interfaces/signingserviceclient.py
index 9077d9c..493164c 100644
--- a/lib/lp/services/signing/interfaces/signingserviceclient.py
+++ b/lib/lp/services/signing/interfaces/signingserviceclient.py
@@ -33,13 +33,14 @@ class ISigningServiceClient(Interface):
33 :return: A dict with 'fingerprint' (str) and 'public-key' (bytes)33 :return: A dict with 'fingerprint' (str) and 'public-key' (bytes)
34 """34 """
3535
36 def sign(key_type, fingerprint, message_name, message, mode):36 def sign(key_type, fingerprints, message_name, message, mode):
37 """Sign the given message using the specified key_type and a37 """Sign the given message using the specified key_type and the
38 pre-generated fingerprint (see `generate` method).38 given pre-generated fingerprints (see`generate` method).
3939
40 :param key_type: One of the key types from SigningKeyType enum40 :param key_type: One of the key types from SigningKeyType enum
41 :param fingerprint: The fingerprint of the signing key, generated by41 :param fingerprints: A list of the fingerprints of one or more
42 the `generate` method42 signing keys, generated by the `generate`
43 method.
43 :param message_name: A description of the message being signed44 :param message_name: A description of the message being signed
44 :param message: The message to be signed45 :param message: The message to be signed
45 :param mode: SigningMode.ATTACHED or SigningMode.DETACHED46 :param mode: SigningMode.ATTACHED or SigningMode.DETACHED
diff --git a/lib/lp/services/signing/model/signingkey.py b/lib/lp/services/signing/model/signingkey.py
index 4bb96c3..b51a081 100644
--- a/lib/lp/services/signing/model/signingkey.py
+++ b/lib/lp/services/signing/model/signingkey.py
@@ -133,15 +133,24 @@ class SigningKey(StormBase):
133 store.add(db_key)133 store.add(db_key)
134 return db_key134 return db_key
135135
136 def sign(self, message, message_name, mode=None):136 @classmethod
137 def sign(cls, signing_keys, message, message_name, mode=None):
138 fingerprints = [key.fingerprint for key in signing_keys]
139 key_type = signing_keys[0].key_type
140 if len(signing_keys) > 1 and not all(
141 key.key_type == key_type for key in signing_keys[1:]
142 ):
143 raise ValueError(
144 "Cannot sign as all the keys are not of the same type."
145 )
137 if mode is None:146 if mode is None:
138 if self.key_type in (SigningKeyType.UEFI, SigningKeyType.FIT):147 if key_type in (SigningKeyType.UEFI, SigningKeyType.FIT):
139 mode = SigningMode.ATTACHED148 mode = SigningMode.ATTACHED
140 else:149 else:
141 mode = SigningMode.DETACHED150 mode = SigningMode.DETACHED
142 signing_service = getUtility(ISigningServiceClient)151 signing_service = getUtility(ISigningServiceClient)
143 signed = signing_service.sign(152 signed = signing_service.sign(
144 self.key_type, self.fingerprint, message_name, message, mode153 key_type, fingerprints, message_name, message, mode
145 )154 )
146 return signed["signed-message"]155 return signed["signed-message"]
147156
@@ -248,6 +257,27 @@ class ArchiveSigningKeySet:
248 )257 )
249258
250 @classmethod259 @classmethod
260 def getOpenPGPSigningKeysForArchive(cls, archive):
261 join = (
262 ArchiveSigningKey,
263 Join(
264 SigningKey,
265 SigningKey.id == ArchiveSigningKey.signing_key_id,
266 ),
267 )
268
269 results = list(
270 IStore(ArchiveSigningKey)
271 .using(*join)
272 .find(
273 SigningKey,
274 ArchiveSigningKey.archive == archive,
275 ArchiveSigningKey.key_type == SigningKeyType.OPENPGP,
276 )
277 )
278 return results
279
280 @classmethod
251 def getByArchiveAndFingerprint(cls, archive, fingerprint):281 def getByArchiveAndFingerprint(cls, archive, fingerprint):
252 join = (282 join = (
253 ArchiveSigningKey,283 ArchiveSigningKey,
diff --git a/lib/lp/services/signing/proxy.py b/lib/lp/services/signing/proxy.py
index 70fefa1..43d01be 100644
--- a/lib/lp/services/signing/proxy.py
+++ b/lib/lp/services/signing/proxy.py
@@ -178,7 +178,7 @@ class SigningServiceClient:
178 "public-key": base64.b64decode(ret["public-key"].encode("UTF-8")),178 "public-key": base64.b64decode(ret["public-key"].encode("UTF-8")),
179 }179 }
180180
181 def sign(self, key_type, fingerprint, message_name, message, mode):181 def sign(self, key_type, fingerprints, message_name, message, mode):
182 valid_modes = {SigningMode.ATTACHED, SigningMode.DETACHED}182 valid_modes = {SigningMode.ATTACHED, SigningMode.DETACHED}
183 if key_type == SigningKeyType.OPENPGP:183 if key_type == SigningKeyType.OPENPGP:
184 valid_modes.add(SigningMode.CLEAR)184 valid_modes.add(SigningMode.CLEAR)
@@ -186,7 +186,20 @@ class SigningServiceClient:
186 raise ValueError("%s is not a valid mode" % mode)186 raise ValueError("%s is not a valid mode" % mode)
187 if key_type not in SigningKeyType.items:187 if key_type not in SigningKeyType.items:
188 raise ValueError("%s is not a valid key type" % key_type)188 raise ValueError("%s is not a valid key type" % key_type)
189 if not fingerprints:
190 raise ValueError("Not even one fingerprint was provided")
191 if len(fingerprints) > 1 and key_type != SigningKeyType.OPENPGP:
192 raise ValueError(
193 "Multi-signing is not supported for non-OpenPGP keys"
194 )
189195
196 # The signing service accepts either a single fingerprint
197 # string (for all key types) or a list of two or more
198 # fingerprints (only for OpenPGP keys) for the 'fingerprint'
199 # property.
200 fingerprint = (
201 fingerprints[0] if len(fingerprints) == 1 else fingerprints
202 )
190 payload = {203 payload = {
191 "key-type": key_type.name,204 "key-type": key_type.name,
192 "fingerprint": fingerprint,205 "fingerprint": fingerprint,
@@ -196,8 +209,14 @@ class SigningServiceClient:
196 }209 }
197210
198 ret = self._requestJson("/sign", "POST", encrypt=True, json=payload)211 ret = self._requestJson("/sign", "POST", encrypt=True, json=payload)
212 if isinstance(ret["public-key"], str):
213 public_key = base64.b64decode(ret["public-key"].encode("UTF-8"))
214 else: # is a list of public key strings
215 public_key = [
216 base64.b64decode(x).encode("UTF-8") for x in ret["public-key"]
217 ]
199 return {218 return {
200 "public-key": base64.b64decode(ret["public-key"].encode("UTF-8")),219 "public-key": public_key,
201 "signed-message": base64.b64decode(220 "signed-message": base64.b64decode(
202 ret["signed-message"].encode("UTF-8")221 ret["signed-message"].encode("UTF-8")
203 ),222 ),
diff --git a/lib/lp/services/signing/tests/test_proxy.py b/lib/lp/services/signing/tests/test_proxy.py
index 78ba505..c639f7e 100644
--- a/lib/lp/services/signing/tests/test_proxy.py
+++ b/lib/lp/services/signing/tests/test_proxy.py
@@ -543,7 +543,9 @@ class SigningServiceProxyTest(TestCaseWithFactory, TestWithFixtures):
543 message = b"this is the message content"543 message = b"this is the message content"
544544
545 signing = getUtility(ISigningServiceClient)545 signing = getUtility(ISigningServiceClient)
546 data = signing.sign(key_type, fingerprint, message_name, message, mode)546 data = signing.sign(
547 key_type, [fingerprint], message_name, message, mode
548 )
547549
548 self.assertEqual(3, len(responses.calls))550 self.assertEqual(3, len(responses.calls))
549 # expected order of HTTP calls551 # expected order of HTTP calls
diff --git a/lib/lp/services/signing/tests/test_signingkey.py b/lib/lp/services/signing/tests/test_signingkey.py
index 327de1e..686456d 100644
--- a/lib/lp/services/signing/tests/test_signingkey.py
+++ b/lib/lp/services/signing/tests/test_signingkey.py
@@ -165,7 +165,8 @@ class TestSigningKey(TestCaseWithFactory, TestWithFixtures):
165 bytes(self.signing_service.generated_public_key),165 bytes(self.signing_service.generated_public_key),
166 description="This is my key!",166 description="This is my key!",
167 )167 )
168 signed = s.sign(b"secure message", "message_name")168 signing_key_set = getUtility(ISigningKeySet)
169 signed = signing_key_set.sign([s], b"secure message", "message_name")
169170
170 # Checks if the returned value is actually the returning value from171 # Checks if the returned value is actually the returning value from
171 # HTTP POST /sign call to lp-signing service172 # HTTP POST /sign call to lp-signing service
@@ -204,8 +205,11 @@ class TestSigningKey(TestCaseWithFactory, TestWithFixtures):
204 bytes(self.signing_service.generated_public_key),205 bytes(self.signing_service.generated_public_key),
205 description="This is my key!",206 description="This is my key!",
206 )207 )
207 s.sign(b"secure message", "message_name")208 signing_key_set = getUtility(ISigningKeySet)
208 s.sign(b"another message", "another_name", mode=SigningMode.CLEAR)209 signing_key_set.sign([s], b"secure message", "message_name")
210 signing_key_set.sign(
211 [s], b"another message", "another_name", mode=SigningMode.CLEAR
212 )
209213
210 self.assertEqual(5, len(responses.calls))214 self.assertEqual(5, len(responses.calls))
211 self.assertThat(215 self.assertThat(

Subscribers

People subscribed via source and target branches

to status/vote changes: