← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Andy Whitcroft has proposed merging lp:~apw/launchpad/signing-record-public-keys-when-used into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~apw/launchpad/signing-record-public-keys-when-used/+merge/296678

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.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~apw/launchpad/signing-record-public-keys-when-used into lp:launchpad.
=== 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-07 14:29:40 +0000
@@ -20,6 +20,7 @@
 
 import os
 import shutil
+import stat
 import subprocess
 import tarfile
 import tempfile
@@ -104,12 +105,30 @@
             dists_signed, "%s-%s" % (self.package, self.arch))
         self.archiveroot = pubconf.archiveroot
 
+        self.public_keys = []
+
+    def publishPublicKey(self, key):
+        """Record this key as having been used in this upload."""
+        if key not in self.public_keys:
+            self.public_keys.append(key)
+
+    def copyPublishedPublicKeys(self):
+        """Copy out published keys into the custom upload."""
+        keydir = os.path.join(self.tmpdir, self.version, "control")
+        if not os.path.exists(keydir):
+            os.makedirs(keydir)
+        for key in self.public_keys:
+            # Ensure we only emit files which are world readable.
+            if stat.S_IMODE(os.stat(key).st_mode) & stat.S_IROTH:
+                shutil.copy(key, os.path.join(keydir, os.path.basename(key)))
+
     def setSigningOptions(self):
-        """Find and extract raw-signing.options from the tarball."""
+        """Find and extract raw-signing options from the tarball."""
         self.signing_options = {}
 
+        # Look for an options file in the top level control directory.
         options_file = os.path.join(self.tmpdir, self.version,
-            "raw-signing.options")
+            "control", "options")
         if not os.path.exists(options_file):
             return
 
@@ -202,6 +221,7 @@
             self.uefi_key, self.uefi_cert)
         if not key or not cert:
             return
+        self.publishPublicKey(cert)
         cmdl = ["sbsign", "--key", key, "--cert", cert, image]
         return self.callLog("UEFI signing", cmdl)
 
@@ -265,6 +285,7 @@
             self.kmod_pem, self.kmod_x509)
         if not pem or not cert:
             return
+        self.publishPublicKey(cert)
         cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
         return self.callLog("Kmod signing", cmdl)
 
@@ -303,6 +324,9 @@
                 'signed-only' in self.signing_options):
                 os.unlink(filename)
 
+        # Copy out the public keys where they were used.
+        self.copyPublishedPublicKeys()
+
         # If tarball output is requested, tar up the results.
         if 'tarball' in self.signing_options:
             self.convertToTarball()

=== 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-07 14:29:40 +0000
@@ -235,7 +235,7 @@
         # If the configured key/cert are missing, processing succeeds but
         # nothing is signed.
         self.openArchive("test", "1.0", "amd64")
-        self.archive.add_file("1.0/raw-signing.options", "")
+        self.archive.add_file("1.0/control/options", "")
         upload = self.process_emulate()
         self.assertContentEqual([], upload.signing_options.keys())
 
@@ -243,7 +243,7 @@
         # If the configured key/cert are missing, processing succeeds but
         # nothing is signed.
         self.openArchive("test", "1.0", "amd64")
-        self.archive.add_file("1.0/raw-signing.options", "first\n")
+        self.archive.add_file("1.0/control/options", "first\n")
         upload = self.process_emulate()
         self.assertContentEqual(['first'], upload.signing_options.keys())
 
@@ -251,7 +251,7 @@
         # If the configured key/cert are missing, processing succeeds but
         # nothing is signed.
         self.openArchive("test", "1.0", "amd64")
-        self.archive.add_file("1.0/raw-signing.options", "first\nsecond\n")
+        self.archive.add_file("1.0/control/options", "first\nsecond\n")
         upload = self.process_emulate()
         self.assertContentEqual(['first', 'second'],
             upload.signing_options.keys())
@@ -279,7 +279,7 @@
         self.setUpUefiKeys()
         self.setUpKmodKeys()
         self.openArchive("test", "1.0", "amd64")
-        self.archive.add_file("1.0/raw-signing.options", "tarball")
+        self.archive.add_file("1.0/control/options", "tarball")
         self.archive.add_file("1.0/empty.efi", "")
         self.archive.add_file("1.0/empty.ko", "")
         self.process_emulate()
@@ -292,8 +292,10 @@
         self.assertTrue(os.path.exists(tarfilename))
         with tarfile.open(tarfilename) as tarball:
             self.assertContentEqual([
-                '1.0', '1.0/empty.efi', '1.0/empty.efi.signed', '1.0/empty.ko',
-                '1.0/empty.ko.sig', '1.0/raw-signing.options',
+                '1.0', '1.0/control', '1.0/control/kmod.x509',
+                '1.0/control/uefi.crt', '1.0/empty.efi',
+                '1.0/empty.efi.signed', '1.0/empty.ko', '1.0/empty.ko.sig',
+                '1.0/control/options',
                 ], tarball.getnames())
 
     def test_options_signed_only(self):
@@ -302,7 +304,7 @@
         self.setUpUefiKeys()
         self.setUpKmodKeys()
         self.openArchive("test", "1.0", "amd64")
-        self.archive.add_file("1.0/raw-signing.options", "signed-only")
+        self.archive.add_file("1.0/control/options", "signed-only")
         self.archive.add_file("1.0/empty.efi", "")
         self.archive.add_file("1.0/empty.ko", "")
         self.process_emulate()
@@ -322,7 +324,7 @@
         self.setUpUefiKeys()
         self.setUpKmodKeys()
         self.openArchive("test", "1.0", "amd64")
-        self.archive.add_file("1.0/raw-signing.options",
+        self.archive.add_file("1.0/control/options",
             "tarball\nsigned-only")
         self.archive.add_file("1.0/empty.efi", "")
         self.archive.add_file("1.0/empty.ko", "")
@@ -332,8 +334,9 @@
         self.assertTrue(os.path.exists(tarfilename))
         with tarfile.open(tarfilename) as tarball:
             self.assertContentEqual([
-                '1.0', '1.0/empty.efi.signed', '1.0/empty.ko.sig',
-                '1.0/raw-signing.options',
+                '1.0', '1.0/control', '1.0/control/kmod.x509',
+                '1.0/control/uefi.crt', '1.0/empty.efi.signed',
+                '1.0/empty.ko.sig', '1.0/control/options',
                 ], tarball.getnames())
 
     def test_no_signed_files(self):


Follow ups