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

Proposed by Andy Whitcroft on 2016-06-07
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 2016-06-07 Approve on 2016-06-10
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.
Colin Watson (cjwatson) :
review: Approve
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
1=== modified file 'lib/lp/archivepublisher/signing.py'
2--- lib/lp/archivepublisher/signing.py 2016-06-06 16:53:57 +0000
3+++ lib/lp/archivepublisher/signing.py 2016-06-10 13:23:21 +0000
4@@ -20,13 +20,13 @@
5
6 import os
7 import shutil
8+import stat
9 import subprocess
10 import tarfile
11 import tempfile
12 import textwrap
13
14 from lp.archivepublisher.customupload import CustomUpload
15-from lp.archivepublisher.utils import RepositoryIndexFile
16 from lp.services.osutils import remove_if_exists
17 from lp.soyuz.interfaces.queue import CustomUploadError
18
19@@ -104,12 +104,33 @@
20 dists_signed, "%s-%s" % (self.package, self.arch))
21 self.archiveroot = pubconf.archiveroot
22
23+ self.public_keys = set()
24+
25+ def publishPublicKey(self, key):
26+ """Record this key as having been used in this upload."""
27+ self.public_keys.add(key)
28+
29+ def copyPublishedPublicKeys(self):
30+ """Copy out published keys into the custom upload."""
31+ keydir = os.path.join(self.tmpdir, self.version, "control")
32+ if not os.path.exists(keydir):
33+ os.makedirs(keydir)
34+ for key in self.public_keys:
35+ # Ensure we only emit files which are world readable.
36+ if stat.S_IMODE(os.stat(key).st_mode) & stat.S_IROTH:
37+ shutil.copy(key, os.path.join(keydir, os.path.basename(key)))
38+ else:
39+ if self.logger is not None:
40+ self.logger.warning(
41+ "%s: public key not world readable" % key)
42+
43 def setSigningOptions(self):
44- """Find and extract raw-signing.options from the tarball."""
45+ """Find and extract raw-signing options from the tarball."""
46 self.signing_options = {}
47
48+ # Look for an options file in the top level control directory.
49 options_file = os.path.join(self.tmpdir, self.version,
50- "raw-signing.options")
51+ "control", "options")
52 if not os.path.exists(options_file):
53 return
54
55@@ -202,6 +223,7 @@
56 self.uefi_key, self.uefi_cert)
57 if not key or not cert:
58 return
59+ self.publishPublicKey(cert)
60 cmdl = ["sbsign", "--key", key, "--cert", cert, image]
61 return self.callLog("UEFI signing", cmdl)
62
63@@ -265,6 +287,7 @@
64 self.kmod_pem, self.kmod_x509)
65 if not pem or not cert:
66 return
67+ self.publishPublicKey(cert)
68 cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
69 return self.callLog("Kmod signing", cmdl)
70
71@@ -303,6 +326,9 @@
72 'signed-only' in self.signing_options):
73 os.unlink(filename)
74
75+ # Copy out the public keys where they were used.
76+ self.copyPublishedPublicKeys()
77+
78 # If tarball output is requested, tar up the results.
79 if 'tarball' in self.signing_options:
80 self.convertToTarball()
81
82=== modified file 'lib/lp/archivepublisher/tests/test_signing.py'
83--- lib/lp/archivepublisher/tests/test_signing.py 2016-06-06 14:26:00 +0000
84+++ lib/lp/archivepublisher/tests/test_signing.py 2016-06-10 13:23:21 +0000
85@@ -235,7 +235,7 @@
86 # If the configured key/cert are missing, processing succeeds but
87 # nothing is signed.
88 self.openArchive("test", "1.0", "amd64")
89- self.archive.add_file("1.0/raw-signing.options", "")
90+ self.archive.add_file("1.0/control/options", "")
91 upload = self.process_emulate()
92 self.assertContentEqual([], upload.signing_options.keys())
93
94@@ -243,7 +243,7 @@
95 # If the configured key/cert are missing, processing succeeds but
96 # nothing is signed.
97 self.openArchive("test", "1.0", "amd64")
98- self.archive.add_file("1.0/raw-signing.options", "first\n")
99+ self.archive.add_file("1.0/control/options", "first\n")
100 upload = self.process_emulate()
101 self.assertContentEqual(['first'], upload.signing_options.keys())
102
103@@ -251,7 +251,7 @@
104 # If the configured key/cert are missing, processing succeeds but
105 # nothing is signed.
106 self.openArchive("test", "1.0", "amd64")
107- self.archive.add_file("1.0/raw-signing.options", "first\nsecond\n")
108+ self.archive.add_file("1.0/control/options", "first\nsecond\n")
109 upload = self.process_emulate()
110 self.assertContentEqual(['first', 'second'],
111 upload.signing_options.keys())
112@@ -279,7 +279,7 @@
113 self.setUpUefiKeys()
114 self.setUpKmodKeys()
115 self.openArchive("test", "1.0", "amd64")
116- self.archive.add_file("1.0/raw-signing.options", "tarball")
117+ self.archive.add_file("1.0/control/options", "tarball")
118 self.archive.add_file("1.0/empty.efi", "")
119 self.archive.add_file("1.0/empty.ko", "")
120 self.process_emulate()
121@@ -292,8 +292,10 @@
122 self.assertTrue(os.path.exists(tarfilename))
123 with tarfile.open(tarfilename) as tarball:
124 self.assertContentEqual([
125- '1.0', '1.0/empty.efi', '1.0/empty.efi.signed', '1.0/empty.ko',
126- '1.0/empty.ko.sig', '1.0/raw-signing.options',
127+ '1.0', '1.0/control', '1.0/control/kmod.x509',
128+ '1.0/control/uefi.crt', '1.0/empty.efi',
129+ '1.0/empty.efi.signed', '1.0/empty.ko', '1.0/empty.ko.sig',
130+ '1.0/control/options',
131 ], tarball.getnames())
132
133 def test_options_signed_only(self):
134@@ -302,7 +304,7 @@
135 self.setUpUefiKeys()
136 self.setUpKmodKeys()
137 self.openArchive("test", "1.0", "amd64")
138- self.archive.add_file("1.0/raw-signing.options", "signed-only")
139+ self.archive.add_file("1.0/control/options", "signed-only")
140 self.archive.add_file("1.0/empty.efi", "")
141 self.archive.add_file("1.0/empty.ko", "")
142 self.process_emulate()
143@@ -322,7 +324,7 @@
144 self.setUpUefiKeys()
145 self.setUpKmodKeys()
146 self.openArchive("test", "1.0", "amd64")
147- self.archive.add_file("1.0/raw-signing.options",
148+ self.archive.add_file("1.0/control/options",
149 "tarball\nsigned-only")
150 self.archive.add_file("1.0/empty.efi", "")
151 self.archive.add_file("1.0/empty.ko", "")
152@@ -332,8 +334,9 @@
153 self.assertTrue(os.path.exists(tarfilename))
154 with tarfile.open(tarfilename) as tarball:
155 self.assertContentEqual([
156- '1.0', '1.0/empty.efi.signed', '1.0/empty.ko.sig',
157- '1.0/raw-signing.options',
158+ '1.0', '1.0/control', '1.0/control/kmod.x509',
159+ '1.0/control/uefi.crt', '1.0/empty.efi.signed',
160+ '1.0/empty.ko.sig', '1.0/control/options',
161 ], tarball.getnames())
162
163 def test_no_signed_files(self):