Merge lp:~apw/launchpad/signing-record-public-keys-when-used into lp:launchpad

Proposed by Andy Whitcroft
Status: Merged
Merged at revision: 18098
Proposed branch: lp:~apw/launchpad/signing-record-public-keys-when-used
Merge into: lp:launchpad
Diff against target: 163 lines (+42/-13)
2 files modified
lib/lp/archivepublisher/signing.py (+29/-3)
lib/lp/archivepublisher/tests/test_signing.py (+13/-10)
To merge this branch: bzr merge lp:~apw/launchpad/signing-record-public-keys-when-used
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+296678@code.launchpad.net

Commit message

Publish the public components of any keys used when signing elements of a raw-signing upload.

Description of the change

Publish the public components of any keys used when signing elements of a raw-signing upload. These public components are published into control/ within the published upload ensuring they are included in the checksum files allowing verification of these keys.

At the same time move the incoming raw-signing.options options file into control/ so that all control information is kept in one directory reducing namespace pollution in the upload. This is deliberately not backwards compatible as there are no existing users of this functionality.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Andy Whitcroft (apw) wrote :

Switched up the public key record to a set(). Added logging for when the public keys are not correctly permissioned. Finally threw out a redundant import reported by lint. Changed all pushed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/archivepublisher/signing.py'
--- lib/lp/archivepublisher/signing.py 2016-06-06 16:53:57 +0000
+++ lib/lp/archivepublisher/signing.py 2016-06-10 13:23:21 +0000
@@ -20,13 +20,13 @@
2020
21import os21import os
22import shutil22import shutil
23import stat
23import subprocess24import subprocess
24import tarfile25import tarfile
25import tempfile26import tempfile
26import textwrap27import textwrap
2728
28from lp.archivepublisher.customupload import CustomUpload29from lp.archivepublisher.customupload import CustomUpload
29from lp.archivepublisher.utils import RepositoryIndexFile
30from lp.services.osutils import remove_if_exists30from lp.services.osutils import remove_if_exists
31from lp.soyuz.interfaces.queue import CustomUploadError31from lp.soyuz.interfaces.queue import CustomUploadError
3232
@@ -104,12 +104,33 @@
104 dists_signed, "%s-%s" % (self.package, self.arch))104 dists_signed, "%s-%s" % (self.package, self.arch))
105 self.archiveroot = pubconf.archiveroot105 self.archiveroot = pubconf.archiveroot
106106
107 self.public_keys = set()
108
109 def publishPublicKey(self, key):
110 """Record this key as having been used in this upload."""
111 self.public_keys.add(key)
112
113 def copyPublishedPublicKeys(self):
114 """Copy out published keys into the custom upload."""
115 keydir = os.path.join(self.tmpdir, self.version, "control")
116 if not os.path.exists(keydir):
117 os.makedirs(keydir)
118 for key in self.public_keys:
119 # Ensure we only emit files which are world readable.
120 if stat.S_IMODE(os.stat(key).st_mode) & stat.S_IROTH:
121 shutil.copy(key, os.path.join(keydir, os.path.basename(key)))
122 else:
123 if self.logger is not None:
124 self.logger.warning(
125 "%s: public key not world readable" % key)
126
107 def setSigningOptions(self):127 def setSigningOptions(self):
108 """Find and extract raw-signing.options from the tarball."""128 """Find and extract raw-signing options from the tarball."""
109 self.signing_options = {}129 self.signing_options = {}
110130
131 # Look for an options file in the top level control directory.
111 options_file = os.path.join(self.tmpdir, self.version,132 options_file = os.path.join(self.tmpdir, self.version,
112 "raw-signing.options")133 "control", "options")
113 if not os.path.exists(options_file):134 if not os.path.exists(options_file):
114 return135 return
115136
@@ -202,6 +223,7 @@
202 self.uefi_key, self.uefi_cert)223 self.uefi_key, self.uefi_cert)
203 if not key or not cert:224 if not key or not cert:
204 return225 return
226 self.publishPublicKey(cert)
205 cmdl = ["sbsign", "--key", key, "--cert", cert, image]227 cmdl = ["sbsign", "--key", key, "--cert", cert, image]
206 return self.callLog("UEFI signing", cmdl)228 return self.callLog("UEFI signing", cmdl)
207229
@@ -265,6 +287,7 @@
265 self.kmod_pem, self.kmod_x509)287 self.kmod_pem, self.kmod_x509)
266 if not pem or not cert:288 if not pem or not cert:
267 return289 return
290 self.publishPublicKey(cert)
268 cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]291 cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
269 return self.callLog("Kmod signing", cmdl)292 return self.callLog("Kmod signing", cmdl)
270293
@@ -303,6 +326,9 @@
303 'signed-only' in self.signing_options):326 'signed-only' in self.signing_options):
304 os.unlink(filename)327 os.unlink(filename)
305328
329 # Copy out the public keys where they were used.
330 self.copyPublishedPublicKeys()
331
306 # If tarball output is requested, tar up the results.332 # If tarball output is requested, tar up the results.
307 if 'tarball' in self.signing_options:333 if 'tarball' in self.signing_options:
308 self.convertToTarball()334 self.convertToTarball()
309335
=== modified file 'lib/lp/archivepublisher/tests/test_signing.py'
--- lib/lp/archivepublisher/tests/test_signing.py 2016-06-06 14:26:00 +0000
+++ lib/lp/archivepublisher/tests/test_signing.py 2016-06-10 13:23:21 +0000
@@ -235,7 +235,7 @@
235 # If the configured key/cert are missing, processing succeeds but235 # If the configured key/cert are missing, processing succeeds but
236 # nothing is signed.236 # nothing is signed.
237 self.openArchive("test", "1.0", "amd64")237 self.openArchive("test", "1.0", "amd64")
238 self.archive.add_file("1.0/raw-signing.options", "")238 self.archive.add_file("1.0/control/options", "")
239 upload = self.process_emulate()239 upload = self.process_emulate()
240 self.assertContentEqual([], upload.signing_options.keys())240 self.assertContentEqual([], upload.signing_options.keys())
241241
@@ -243,7 +243,7 @@
243 # If the configured key/cert are missing, processing succeeds but243 # If the configured key/cert are missing, processing succeeds but
244 # nothing is signed.244 # nothing is signed.
245 self.openArchive("test", "1.0", "amd64")245 self.openArchive("test", "1.0", "amd64")
246 self.archive.add_file("1.0/raw-signing.options", "first\n")246 self.archive.add_file("1.0/control/options", "first\n")
247 upload = self.process_emulate()247 upload = self.process_emulate()
248 self.assertContentEqual(['first'], upload.signing_options.keys())248 self.assertContentEqual(['first'], upload.signing_options.keys())
249249
@@ -251,7 +251,7 @@
251 # If the configured key/cert are missing, processing succeeds but251 # If the configured key/cert are missing, processing succeeds but
252 # nothing is signed.252 # nothing is signed.
253 self.openArchive("test", "1.0", "amd64")253 self.openArchive("test", "1.0", "amd64")
254 self.archive.add_file("1.0/raw-signing.options", "first\nsecond\n")254 self.archive.add_file("1.0/control/options", "first\nsecond\n")
255 upload = self.process_emulate()255 upload = self.process_emulate()
256 self.assertContentEqual(['first', 'second'],256 self.assertContentEqual(['first', 'second'],
257 upload.signing_options.keys())257 upload.signing_options.keys())
@@ -279,7 +279,7 @@
279 self.setUpUefiKeys()279 self.setUpUefiKeys()
280 self.setUpKmodKeys()280 self.setUpKmodKeys()
281 self.openArchive("test", "1.0", "amd64")281 self.openArchive("test", "1.0", "amd64")
282 self.archive.add_file("1.0/raw-signing.options", "tarball")282 self.archive.add_file("1.0/control/options", "tarball")
283 self.archive.add_file("1.0/empty.efi", "")283 self.archive.add_file("1.0/empty.efi", "")
284 self.archive.add_file("1.0/empty.ko", "")284 self.archive.add_file("1.0/empty.ko", "")
285 self.process_emulate()285 self.process_emulate()
@@ -292,8 +292,10 @@
292 self.assertTrue(os.path.exists(tarfilename))292 self.assertTrue(os.path.exists(tarfilename))
293 with tarfile.open(tarfilename) as tarball:293 with tarfile.open(tarfilename) as tarball:
294 self.assertContentEqual([294 self.assertContentEqual([
295 '1.0', '1.0/empty.efi', '1.0/empty.efi.signed', '1.0/empty.ko',295 '1.0', '1.0/control', '1.0/control/kmod.x509',
296 '1.0/empty.ko.sig', '1.0/raw-signing.options',296 '1.0/control/uefi.crt', '1.0/empty.efi',
297 '1.0/empty.efi.signed', '1.0/empty.ko', '1.0/empty.ko.sig',
298 '1.0/control/options',
297 ], tarball.getnames())299 ], tarball.getnames())
298300
299 def test_options_signed_only(self):301 def test_options_signed_only(self):
@@ -302,7 +304,7 @@
302 self.setUpUefiKeys()304 self.setUpUefiKeys()
303 self.setUpKmodKeys()305 self.setUpKmodKeys()
304 self.openArchive("test", "1.0", "amd64")306 self.openArchive("test", "1.0", "amd64")
305 self.archive.add_file("1.0/raw-signing.options", "signed-only")307 self.archive.add_file("1.0/control/options", "signed-only")
306 self.archive.add_file("1.0/empty.efi", "")308 self.archive.add_file("1.0/empty.efi", "")
307 self.archive.add_file("1.0/empty.ko", "")309 self.archive.add_file("1.0/empty.ko", "")
308 self.process_emulate()310 self.process_emulate()
@@ -322,7 +324,7 @@
322 self.setUpUefiKeys()324 self.setUpUefiKeys()
323 self.setUpKmodKeys()325 self.setUpKmodKeys()
324 self.openArchive("test", "1.0", "amd64")326 self.openArchive("test", "1.0", "amd64")
325 self.archive.add_file("1.0/raw-signing.options",327 self.archive.add_file("1.0/control/options",
326 "tarball\nsigned-only")328 "tarball\nsigned-only")
327 self.archive.add_file("1.0/empty.efi", "")329 self.archive.add_file("1.0/empty.efi", "")
328 self.archive.add_file("1.0/empty.ko", "")330 self.archive.add_file("1.0/empty.ko", "")
@@ -332,8 +334,9 @@
332 self.assertTrue(os.path.exists(tarfilename))334 self.assertTrue(os.path.exists(tarfilename))
333 with tarfile.open(tarfilename) as tarball:335 with tarfile.open(tarfilename) as tarball:
334 self.assertContentEqual([336 self.assertContentEqual([
335 '1.0', '1.0/empty.efi.signed', '1.0/empty.ko.sig',337 '1.0', '1.0/control', '1.0/control/kmod.x509',
336 '1.0/raw-signing.options',338 '1.0/control/uefi.crt', '1.0/empty.efi.signed',
339 '1.0/empty.ko.sig', '1.0/control/options',
337 ], tarball.getnames())340 ], tarball.getnames())
338341
339 def test_no_signed_files(self):342 def test_no_signed_files(self):