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
1diff --git a/lib/lp/archivepublisher/archivegpgsigningkey.py b/lib/lp/archivepublisher/archivegpgsigningkey.py
2index f24322d..34267a4 100644
3--- a/lib/lp/archivepublisher/archivegpgsigningkey.py
4+++ b/lib/lp/archivepublisher/archivegpgsigningkey.py
5@@ -72,16 +72,29 @@ class SignableArchive:
6 )
7
8 @cachedproperty
9- def _signing_key(self):
10- """This archive's signing key on the signing service, if any."""
11+ def _signing_keys(self):
12+ """This archive's signing keys on the signing service, if any."""
13 if not getFeatureFlag(PUBLISHER_GPG_USES_SIGNING_SERVICE):
14- return None
15- elif self.archive.signing_key_fingerprint is not None:
16- return getUtility(ISigningKeySet).get(
17- SigningKeyType.OPENPGP, self.archive.signing_key_fingerprint
18+ return []
19+
20+ signing_keys = []
21+ signing_keys.extend(
22+ getUtility(IArchiveSigningKeySet).getOpenPGPSigningKeysForArchive(
23+ self.archive,
24 )
25- else:
26- return None
27+ )
28+ if self.archive.signing_key_fingerprint is not None:
29+ if not any(
30+ key.fingerprint == self.archive.signing_key_fingerprint
31+ for key in signing_keys
32+ ):
33+ signing_key = getUtility(ISigningKeySet).get(
34+ SigningKeyType.OPENPGP,
35+ self.archive.signing_key_fingerprint,
36+ )
37+ if signing_key:
38+ signing_keys.append(signing_key)
39+ return signing_keys
40
41 @cachedproperty
42 def _secret_key(self):
43@@ -121,12 +134,13 @@ class SignableArchive:
44 raise ValueError("Invalid signature mode for GPG: %s" % mode)
45 signed = False
46
47- if self._signing_key is not None or self._secret_key is not None:
48+ if self._signing_keys or self._secret_key is not None:
49 with open(input_path, "rb") as input_file:
50 input_content = input_file.read()
51- if self._signing_key is not None:
52+ if self._signing_keys:
53 try:
54- signature = self._signing_key.sign(
55+ signature = getUtility(ISigningKeySet).sign(
56+ self._signing_keys,
57 input_content,
58 os.path.basename(input_path),
59 mode=mode,
60@@ -138,7 +152,7 @@ class SignableArchive:
61 "Failed to sign archive using signing "
62 "service; falling back to local key"
63 )
64- get_property_cache(self)._signing_key = None
65+ get_property_cache(self)._signing_keys = None
66 if not signed and self._secret_key is not None:
67 signature = getUtility(IGPGHandler).signContent(
68 input_content,
69diff --git a/lib/lp/archivepublisher/signing.py b/lib/lp/archivepublisher/signing.py
70index 9938a95..899ddf1 100644
71--- a/lib/lp/archivepublisher/signing.py
72+++ b/lib/lp/archivepublisher/signing.py
73@@ -32,7 +32,10 @@ from lp.registry.interfaces.distroseries import IDistroSeriesSet
74 from lp.services.features import getFeatureFlag
75 from lp.services.osutils import remove_if_exists
76 from lp.services.signing.enums import SigningKeyType
77-from lp.services.signing.interfaces.signingkey import IArchiveSigningKeySet
78+from lp.services.signing.interfaces.signingkey import (
79+ IArchiveSigningKeySet,
80+ ISigningKeySet,
81+)
82 from lp.soyuz.interfaces.queue import CustomUploadError
83
84 PUBLISHER_USES_SIGNING_SERVICE = "archivepublisher.signing_service.enabled"
85@@ -398,10 +401,9 @@ class SigningUpload(CustomUpload):
86
87 with open(filename, "rb") as fd:
88 content = fd.read()
89-
90 try:
91- signed_content = signing_key.sign(
92- content, message_name=os.path.basename(filename)
93+ signed_content = getUtility(ISigningKeySet).sign(
94+ [signing_key], content, message_name=os.path.basename(filename)
95 )
96 except Exception as e:
97 if self.logger:
98diff --git a/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py b/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py
99index e51077a..ff2ce27 100644
100--- a/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py
101+++ b/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py
102@@ -38,7 +38,10 @@ from lp.services.gpg.tests.test_gpghandler import FakeGenerateKey
103 from lp.services.log.logger import BufferLogger
104 from lp.services.osutils import write_file
105 from lp.services.signing.enums import SigningKeyType, SigningMode
106-from lp.services.signing.interfaces.signingkey import ISigningKeySet
107+from lp.services.signing.interfaces.signingkey import (
108+ IArchiveSigningKeySet,
109+ ISigningKeySet,
110+)
111 from lp.services.signing.tests.helpers import SigningServiceClientFixture
112 from lp.services.twistedsupport.testing import TReqFixture
113 from lp.services.twistedsupport.treq import check_status
114@@ -146,14 +149,14 @@ class TestSignableArchiveWithSigningKey(TestCaseWithFactory):
115 [
116 mock.call(
117 SigningKeyType.OPENPGP,
118- self.archive.signing_key_fingerprint,
119+ [self.archive.signing_key_fingerprint],
120 "Release",
121 b"Release contents",
122 SigningMode.DETACHED,
123 ),
124 mock.call(
125 SigningKeyType.OPENPGP,
126- self.archive.signing_key_fingerprint,
127+ [self.archive.signing_key_fingerprint],
128 "Release",
129 b"Release contents",
130 SigningMode.CLEAR,
131@@ -169,6 +172,60 @@ class TestSignableArchiveWithSigningKey(TestCaseWithFactory):
132 FileContains("signed with key_type=OPENPGP mode=CLEAR"),
133 )
134
135+ def test_signRepository_uses_all_OpenPGPG_keys_of_an_archive(self):
136+ self.useFixture(
137+ FeatureFixture({PUBLISHER_GPG_USES_SIGNING_SERVICE: "on"})
138+ )
139+ current_key = self.factory.makeSigningKey(
140+ key_type=SigningKeyType.OPENPGP,
141+ fingerprint=self.archive.signing_key_fingerprint,
142+ )
143+ new_key = self.factory.makeSigningKey(
144+ key_type=SigningKeyType.OPENPGP,
145+ # The fingerprint has to be 30 characters long to pass validation.
146+ fingerprint="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234",
147+ )
148+ self.archive.signing_key_fingerprint = current_key.fingerprint
149+ getUtility(IArchiveSigningKeySet).create(
150+ self.archive, None, current_key
151+ )
152+ getUtility(IArchiveSigningKeySet).create(self.archive, None, new_key)
153+ signing_service_client = self.useFixture(
154+ SigningServiceClientFixture(self.factory)
155+ )
156+ suite_dir = os.path.join(
157+ self.archive_root,
158+ "dists",
159+ self.suite,
160+ )
161+ release_path = os.path.join(suite_dir, "Release")
162+ write_file(release_path, b"Release contents")
163+ logger = BufferLogger()
164+ signer = ISignableArchive(self.archive)
165+ self.assertTrue(signer.can_sign)
166+ self.assertContentEqual(
167+ ["Release.gpg", "InRelease"],
168+ signer.signRepository(self.suite, log=logger),
169+ )
170+ signing_service_client.sign.assert_has_calls(
171+ [
172+ mock.call(
173+ SigningKeyType.OPENPGP,
174+ [current_key.fingerprint, new_key.fingerprint],
175+ "Release",
176+ b"Release contents",
177+ SigningMode.DETACHED,
178+ ),
179+ mock.call(
180+ SigningKeyType.OPENPGP,
181+ [current_key.fingerprint, new_key.fingerprint],
182+ "Release",
183+ b"Release contents",
184+ SigningMode.CLEAR,
185+ ),
186+ ]
187+ )
188+
189 def test_signRepository_falls_back_from_signing_service(self):
190 # If the signing service fails to sign a file, we fall back to
191 # making local signatures if possible.
192@@ -202,7 +259,7 @@ class TestSignableArchiveWithSigningKey(TestCaseWithFactory):
193 )
194 signing_service_client.sign.assert_called_once_with(
195 SigningKeyType.OPENPGP,
196- self.archive.signing_key_fingerprint,
197+ [self.archive.signing_key_fingerprint],
198 "Release",
199 b"Release contents",
200 SigningMode.DETACHED,
201diff --git a/lib/lp/archivepublisher/tests/test_signing.py b/lib/lp/archivepublisher/tests/test_signing.py
202index c352f3d..91e40cc 100644
203--- a/lib/lp/archivepublisher/tests/test_signing.py
204+++ b/lib/lp/archivepublisher/tests/test_signing.py
205@@ -1955,49 +1955,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
206 [
207 call(
208 SigningKeyType.UEFI,
209- keys[SigningKeyType.UEFI].fingerprint,
210+ [keys[SigningKeyType.UEFI].fingerprint],
211 "empty.efi",
212 b"a",
213 SigningMode.ATTACHED,
214 ),
215 call(
216 SigningKeyType.KMOD,
217- keys[SigningKeyType.KMOD].fingerprint,
218+ [keys[SigningKeyType.KMOD].fingerprint],
219 "empty.ko",
220 b"b",
221 SigningMode.DETACHED,
222 ),
223 call(
224 SigningKeyType.OPAL,
225- keys[SigningKeyType.OPAL].fingerprint,
226+ [keys[SigningKeyType.OPAL].fingerprint],
227 "empty.opal",
228 b"c",
229 SigningMode.DETACHED,
230 ),
231 call(
232 SigningKeyType.SIPL,
233- keys[SigningKeyType.SIPL].fingerprint,
234+ [keys[SigningKeyType.SIPL].fingerprint],
235 "empty.sipl",
236 b"d",
237 SigningMode.DETACHED,
238 ),
239 call(
240 SigningKeyType.FIT,
241- keys[SigningKeyType.FIT].fingerprint,
242+ [keys[SigningKeyType.FIT].fingerprint],
243 "empty.fit",
244 b"e",
245 SigningMode.ATTACHED,
246 ),
247 call(
248 SigningKeyType.CV2_KERNEL,
249- keys[SigningKeyType.CV2_KERNEL].fingerprint,
250+ [keys[SigningKeyType.CV2_KERNEL].fingerprint],
251 "empty.cv2-kernel",
252 b"f",
253 SigningMode.DETACHED,
254 ),
255 call(
256 SigningKeyType.ANDROID_KERNEL,
257- keys[SigningKeyType.ANDROID_KERNEL].fingerprint,
258+ [keys[SigningKeyType.ANDROID_KERNEL].fingerprint],
259 "empty.android-kernel",
260 b"g",
261 SigningMode.DETACHED,
262@@ -2051,49 +2051,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
263 [
264 call(
265 SigningKeyType.UEFI,
266- keys[SigningKeyType.UEFI].fingerprint,
267+ [keys[SigningKeyType.UEFI].fingerprint],
268 "empty.efi",
269 b"a",
270 SigningMode.ATTACHED,
271 ),
272 call(
273 SigningKeyType.KMOD,
274- keys[SigningKeyType.KMOD].fingerprint,
275+ [keys[SigningKeyType.KMOD].fingerprint],
276 "empty.ko",
277 b"b",
278 SigningMode.DETACHED,
279 ),
280 call(
281 SigningKeyType.OPAL,
282- keys[SigningKeyType.OPAL].fingerprint,
283+ [keys[SigningKeyType.OPAL].fingerprint],
284 "empty.opal",
285 b"c",
286 SigningMode.DETACHED,
287 ),
288 call(
289 SigningKeyType.SIPL,
290- keys[SigningKeyType.SIPL].fingerprint,
291+ [keys[SigningKeyType.SIPL].fingerprint],
292 "empty.sipl",
293 b"d",
294 SigningMode.DETACHED,
295 ),
296 call(
297 SigningKeyType.FIT,
298- keys[SigningKeyType.FIT].fingerprint,
299+ [keys[SigningKeyType.FIT].fingerprint],
300 "empty.fit",
301 b"e",
302 SigningMode.ATTACHED,
303 ),
304 call(
305 SigningKeyType.CV2_KERNEL,
306- keys[SigningKeyType.CV2_KERNEL].fingerprint,
307+ [keys[SigningKeyType.CV2_KERNEL].fingerprint],
308 "empty.cv2-kernel",
309 b"f",
310 SigningMode.DETACHED,
311 ),
312 call(
313 SigningKeyType.ANDROID_KERNEL,
314- keys[SigningKeyType.ANDROID_KERNEL].fingerprint,
315+ [keys[SigningKeyType.ANDROID_KERNEL].fingerprint],
316 "empty.android-kernel",
317 b"g",
318 SigningMode.DETACHED,
319@@ -2158,49 +2158,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
320 [
321 call(
322 SigningKeyType.UEFI,
323- keys[SigningKeyType.UEFI].fingerprint,
324+ [keys[SigningKeyType.UEFI].fingerprint],
325 "empty.efi",
326 b"a",
327 SigningMode.ATTACHED,
328 ),
329 call(
330 SigningKeyType.KMOD,
331- keys[SigningKeyType.KMOD].fingerprint,
332+ [keys[SigningKeyType.KMOD].fingerprint],
333 "empty.ko",
334 b"b",
335 SigningMode.DETACHED,
336 ),
337 call(
338 SigningKeyType.OPAL,
339- keys[SigningKeyType.OPAL].fingerprint,
340+ [keys[SigningKeyType.OPAL].fingerprint],
341 "empty.opal",
342 b"c",
343 SigningMode.DETACHED,
344 ),
345 call(
346 SigningKeyType.SIPL,
347- keys[SigningKeyType.SIPL].fingerprint,
348+ [keys[SigningKeyType.SIPL].fingerprint],
349 "empty.sipl",
350 b"d",
351 SigningMode.DETACHED,
352 ),
353 call(
354 SigningKeyType.FIT,
355- keys[SigningKeyType.FIT].fingerprint,
356+ [keys[SigningKeyType.FIT].fingerprint],
357 "empty.fit",
358 b"e",
359 SigningMode.ATTACHED,
360 ),
361 call(
362 SigningKeyType.CV2_KERNEL,
363- keys[SigningKeyType.CV2_KERNEL].fingerprint,
364+ [keys[SigningKeyType.CV2_KERNEL].fingerprint],
365 "empty.cv2-kernel",
366 b"f",
367 SigningMode.DETACHED,
368 ),
369 call(
370 SigningKeyType.ANDROID_KERNEL,
371- keys[SigningKeyType.ANDROID_KERNEL].fingerprint,
372+ [keys[SigningKeyType.ANDROID_KERNEL].fingerprint],
373 "empty.android-kernel",
374 b"g",
375 SigningMode.DETACHED,
376@@ -2323,14 +2323,14 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
377 [
378 call(
379 SigningKeyType.KMOD,
380- keys[SigningKeyType.KMOD].fingerprint,
381+ [keys[SigningKeyType.KMOD].fingerprint],
382 "empty.ko",
383 b"some data for 1.0/empty.ko",
384 SigningMode.DETACHED,
385 ),
386 call(
387 SigningKeyType.OPAL,
388- keys[SigningKeyType.OPAL].fingerprint,
389+ [keys[SigningKeyType.OPAL].fingerprint],
390 "empty.opal",
391 b"some data for 1.0/empty.opal",
392 SigningMode.DETACHED,
393@@ -2410,49 +2410,49 @@ class TestSigningUploadWithSigningService(TestSigningHelpers):
394 [
395 call(
396 SigningKeyType.UEFI,
397- fingerprints[SigningKeyType.UEFI],
398+ [fingerprints[SigningKeyType.UEFI]],
399 "empty.efi",
400 b"data - 1.0/empty.efi",
401 SigningMode.ATTACHED,
402 ),
403 call(
404 SigningKeyType.KMOD,
405- fingerprints[SigningKeyType.KMOD],
406+ [fingerprints[SigningKeyType.KMOD]],
407 "empty.ko",
408 b"data - 1.0/empty.ko",
409 SigningMode.DETACHED,
410 ),
411 call(
412 SigningKeyType.OPAL,
413- fingerprints[SigningKeyType.OPAL],
414+ [fingerprints[SigningKeyType.OPAL]],
415 "empty.opal",
416 b"data - 1.0/empty.opal",
417 SigningMode.DETACHED,
418 ),
419 call(
420 SigningKeyType.SIPL,
421- fingerprints[SigningKeyType.SIPL],
422+ [fingerprints[SigningKeyType.SIPL]],
423 "empty.sipl",
424 b"data - 1.0/empty.sipl",
425 SigningMode.DETACHED,
426 ),
427 call(
428 SigningKeyType.FIT,
429- fingerprints[SigningKeyType.FIT],
430+ [fingerprints[SigningKeyType.FIT]],
431 "empty.fit",
432 b"data - 1.0/empty.fit",
433 SigningMode.ATTACHED,
434 ),
435 call(
436 SigningKeyType.CV2_KERNEL,
437- fingerprints[SigningKeyType.CV2_KERNEL],
438+ [fingerprints[SigningKeyType.CV2_KERNEL]],
439 "empty.cv2-kernel",
440 b"data - 1.0/empty.cv2-kernel",
441 SigningMode.DETACHED,
442 ),
443 call(
444 SigningKeyType.ANDROID_KERNEL,
445- fingerprints[SigningKeyType.ANDROID_KERNEL],
446+ [fingerprints[SigningKeyType.ANDROID_KERNEL]],
447 "empty.android-kernel",
448 b"data - 1.0/empty.android-kernel",
449 SigningMode.DETACHED,
450diff --git a/lib/lp/services/signing/interfaces/signingkey.py b/lib/lp/services/signing/interfaces/signingkey.py
451index a99e018..b28d703 100644
452--- a/lib/lp/services/signing/interfaces/signingkey.py
453+++ b/lib/lp/services/signing/interfaces/signingkey.py
454@@ -44,16 +44,6 @@ class ISigningKey(Interface):
455 title=_("When this key was created"), required=True, readonly=True
456 )
457
458- def sign(message, message_name, mode=None):
459- """Sign the given message using this key
460-
461- :param message: The message to be signed.
462- :param message_name: A name for the message being signed.
463- :param mode: A `SigningMode` specifying how the message is to be
464- signed. Defaults to `SigningMode.ATTACHED` for UEFI and FIT
465- keys, and `SigningMode.DETACHED` for other key types.
466- """
467-
468 def addAuthorization(client_name):
469 """Authorize another client to use this key.
470
471@@ -101,6 +91,19 @@ class ISigningKeySet(Interface):
472 key at lp-signing
473 """
474
475+ def sign(signing_keys, message, message_name, mode=None):
476+ """Sign the given message using the given keys
477+
478+ :param signing_keys: A list of one or more signing keys to sign
479+ the given message with. If more than one signing key is provided,
480+ all signing keys must be of the same type.
481+ :param message: The message to be signed.
482+ :param message_name: A name for the message being signed.
483+ :param mode: A `SigningMode` specifying how the message is to be
484+ signed. Defaults to `SigningMode.ATTACHED` for UEFI and FIT
485+ keys, and `SigningMode.DETACHED` for other key types.
486+ """
487+
488
489 class IArchiveSigningKey(Interface):
490 """Which signing key should be used by a specific archive"""
491@@ -162,6 +165,13 @@ class IArchiveSigningKeySet(Interface):
492 :return: The most suitable key, or None.
493 """
494
495+ def getOpenPGPSigningKeysForArchive(archive):
496+ """Find and return the OpenPGP signing keys for the given archive.
497+
498+ :param archive: The archive to get the OpenPGP signing keys for.
499+ :return: A list of matching signing keys or an empty list.
500+ """
501+
502 def getByArchiveAndFingerprint(archive, fingerprint):
503 """Get ArchiveSigningKey by archive and fingerprint.
504
505diff --git a/lib/lp/services/signing/interfaces/signingserviceclient.py b/lib/lp/services/signing/interfaces/signingserviceclient.py
506index 9077d9c..493164c 100644
507--- a/lib/lp/services/signing/interfaces/signingserviceclient.py
508+++ b/lib/lp/services/signing/interfaces/signingserviceclient.py
509@@ -33,13 +33,14 @@ class ISigningServiceClient(Interface):
510 :return: A dict with 'fingerprint' (str) and 'public-key' (bytes)
511 """
512
513- def sign(key_type, fingerprint, message_name, message, mode):
514- """Sign the given message using the specified key_type and a
515- pre-generated fingerprint (see `generate` method).
516+ def sign(key_type, fingerprints, message_name, message, mode):
517+ """Sign the given message using the specified key_type and the
518+ given pre-generated fingerprints (see`generate` method).
519
520 :param key_type: One of the key types from SigningKeyType enum
521- :param fingerprint: The fingerprint of the signing key, generated by
522- the `generate` method
523+ :param fingerprints: A list of the fingerprints of one or more
524+ signing keys, generated by the `generate`
525+ method.
526 :param message_name: A description of the message being signed
527 :param message: The message to be signed
528 :param mode: SigningMode.ATTACHED or SigningMode.DETACHED
529diff --git a/lib/lp/services/signing/model/signingkey.py b/lib/lp/services/signing/model/signingkey.py
530index 4bb96c3..b51a081 100644
531--- a/lib/lp/services/signing/model/signingkey.py
532+++ b/lib/lp/services/signing/model/signingkey.py
533@@ -133,15 +133,24 @@ class SigningKey(StormBase):
534 store.add(db_key)
535 return db_key
536
537- def sign(self, message, message_name, mode=None):
538+ @classmethod
539+ def sign(cls, signing_keys, message, message_name, mode=None):
540+ fingerprints = [key.fingerprint for key in signing_keys]
541+ key_type = signing_keys[0].key_type
542+ if len(signing_keys) > 1 and not all(
543+ key.key_type == key_type for key in signing_keys[1:]
544+ ):
545+ raise ValueError(
546+ "Cannot sign as all the keys are not of the same type."
547+ )
548 if mode is None:
549- if self.key_type in (SigningKeyType.UEFI, SigningKeyType.FIT):
550+ if key_type in (SigningKeyType.UEFI, SigningKeyType.FIT):
551 mode = SigningMode.ATTACHED
552 else:
553 mode = SigningMode.DETACHED
554 signing_service = getUtility(ISigningServiceClient)
555 signed = signing_service.sign(
556- self.key_type, self.fingerprint, message_name, message, mode
557+ key_type, fingerprints, message_name, message, mode
558 )
559 return signed["signed-message"]
560
561@@ -248,6 +257,27 @@ class ArchiveSigningKeySet:
562 )
563
564 @classmethod
565+ def getOpenPGPSigningKeysForArchive(cls, archive):
566+ join = (
567+ ArchiveSigningKey,
568+ Join(
569+ SigningKey,
570+ SigningKey.id == ArchiveSigningKey.signing_key_id,
571+ ),
572+ )
573+
574+ results = list(
575+ IStore(ArchiveSigningKey)
576+ .using(*join)
577+ .find(
578+ SigningKey,
579+ ArchiveSigningKey.archive == archive,
580+ ArchiveSigningKey.key_type == SigningKeyType.OPENPGP,
581+ )
582+ )
583+ return results
584+
585+ @classmethod
586 def getByArchiveAndFingerprint(cls, archive, fingerprint):
587 join = (
588 ArchiveSigningKey,
589diff --git a/lib/lp/services/signing/proxy.py b/lib/lp/services/signing/proxy.py
590index 70fefa1..43d01be 100644
591--- a/lib/lp/services/signing/proxy.py
592+++ b/lib/lp/services/signing/proxy.py
593@@ -178,7 +178,7 @@ class SigningServiceClient:
594 "public-key": base64.b64decode(ret["public-key"].encode("UTF-8")),
595 }
596
597- def sign(self, key_type, fingerprint, message_name, message, mode):
598+ def sign(self, key_type, fingerprints, message_name, message, mode):
599 valid_modes = {SigningMode.ATTACHED, SigningMode.DETACHED}
600 if key_type == SigningKeyType.OPENPGP:
601 valid_modes.add(SigningMode.CLEAR)
602@@ -186,7 +186,20 @@ class SigningServiceClient:
603 raise ValueError("%s is not a valid mode" % mode)
604 if key_type not in SigningKeyType.items:
605 raise ValueError("%s is not a valid key type" % key_type)
606+ if not fingerprints:
607+ raise ValueError("Not even one fingerprint was provided")
608+ if len(fingerprints) > 1 and key_type != SigningKeyType.OPENPGP:
609+ raise ValueError(
610+ "Multi-signing is not supported for non-OpenPGP keys"
611+ )
612
613+ # The signing service accepts either a single fingerprint
614+ # string (for all key types) or a list of two or more
615+ # fingerprints (only for OpenPGP keys) for the 'fingerprint'
616+ # property.
617+ fingerprint = (
618+ fingerprints[0] if len(fingerprints) == 1 else fingerprints
619+ )
620 payload = {
621 "key-type": key_type.name,
622 "fingerprint": fingerprint,
623@@ -196,8 +209,14 @@ class SigningServiceClient:
624 }
625
626 ret = self._requestJson("/sign", "POST", encrypt=True, json=payload)
627+ if isinstance(ret["public-key"], str):
628+ public_key = base64.b64decode(ret["public-key"].encode("UTF-8"))
629+ else: # is a list of public key strings
630+ public_key = [
631+ base64.b64decode(x).encode("UTF-8") for x in ret["public-key"]
632+ ]
633 return {
634- "public-key": base64.b64decode(ret["public-key"].encode("UTF-8")),
635+ "public-key": public_key,
636 "signed-message": base64.b64decode(
637 ret["signed-message"].encode("UTF-8")
638 ),
639diff --git a/lib/lp/services/signing/tests/test_proxy.py b/lib/lp/services/signing/tests/test_proxy.py
640index 78ba505..c639f7e 100644
641--- a/lib/lp/services/signing/tests/test_proxy.py
642+++ b/lib/lp/services/signing/tests/test_proxy.py
643@@ -543,7 +543,9 @@ class SigningServiceProxyTest(TestCaseWithFactory, TestWithFixtures):
644 message = b"this is the message content"
645
646 signing = getUtility(ISigningServiceClient)
647- data = signing.sign(key_type, fingerprint, message_name, message, mode)
648+ data = signing.sign(
649+ key_type, [fingerprint], message_name, message, mode
650+ )
651
652 self.assertEqual(3, len(responses.calls))
653 # expected order of HTTP calls
654diff --git a/lib/lp/services/signing/tests/test_signingkey.py b/lib/lp/services/signing/tests/test_signingkey.py
655index 327de1e..686456d 100644
656--- a/lib/lp/services/signing/tests/test_signingkey.py
657+++ b/lib/lp/services/signing/tests/test_signingkey.py
658@@ -165,7 +165,8 @@ class TestSigningKey(TestCaseWithFactory, TestWithFixtures):
659 bytes(self.signing_service.generated_public_key),
660 description="This is my key!",
661 )
662- signed = s.sign(b"secure message", "message_name")
663+ signing_key_set = getUtility(ISigningKeySet)
664+ signed = signing_key_set.sign([s], b"secure message", "message_name")
665
666 # Checks if the returned value is actually the returning value from
667 # HTTP POST /sign call to lp-signing service
668@@ -204,8 +205,11 @@ class TestSigningKey(TestCaseWithFactory, TestWithFixtures):
669 bytes(self.signing_service.generated_public_key),
670 description="This is my key!",
671 )
672- s.sign(b"secure message", "message_name")
673- s.sign(b"another message", "another_name", mode=SigningMode.CLEAR)
674+ signing_key_set = getUtility(ISigningKeySet)
675+ signing_key_set.sign([s], b"secure message", "message_name")
676+ signing_key_set.sign(
677+ [s], b"another message", "another_name", mode=SigningMode.CLEAR
678+ )
679
680 self.assertEqual(5, len(responses.calls))
681 self.assertThat(

Subscribers

People subscribed via source and target branches

to status/vote changes: