Merge lp:~apw/launchpad/signing-fit into lp:launchpad
- signing-fit
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 18987 | ||||
Proposed branch: | lp:~apw/launchpad/signing-fit | ||||
Merge into: | lp:launchpad | ||||
Prerequisite: | lp:~apw/launchpad/signing-sipl | ||||
Diff against target: |
513 lines (+208/-15) 2 files modified
lib/lp/archivepublisher/signing.py (+42/-9) lib/lp/archivepublisher/tests/test_signing.py (+166/-6) |
||||
To merge this branch: | bzr merge lp:~apw/launchpad/signing-fit | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+368277@code.launchpad.net |
This proposal supersedes a proposal from 2019-06-03.
Commit message
Add u-boot Flat Image Tree signing support.
Description of the change
u-boot supports generation and verification of FIT images in a similar fashion to secure boot. These signatures are expressed as signature nodes in the DTB contained within the FIT image. Add support for performing FIT signing against *.fit files in the signing custom uploads.
Colin Watson (cjwatson) wrote : | # |
The launchpad-
Andy Whitcroft (apw) wrote : | # |
The u-boot updates containing the key-name hardening are now all released to -updates:
u-boot | 2016.01+
u-boot | 2018.07~
u-boot | 2018.07~
u-boot | 2018.07~
Preview Diff
1 | === modified file 'lib/lp/archivepublisher/signing.py' |
2 | --- lib/lp/archivepublisher/signing.py 2019-06-06 10:57:20 +0000 |
3 | +++ lib/lp/archivepublisher/signing.py 2019-06-06 10:57:20 +0000 |
4 | @@ -97,6 +97,8 @@ |
5 | self.opal_x509 = None |
6 | self.sipl_pem = None |
7 | self.sipl_x509 = None |
8 | + self.fit_key = None |
9 | + self.fit_cert = None |
10 | self.autokey = False |
11 | else: |
12 | self.uefi_key = os.path.join(pubconf.signingroot, "uefi.key") |
13 | @@ -107,6 +109,11 @@ |
14 | self.opal_x509 = os.path.join(pubconf.signingroot, "opal.x509") |
15 | self.sipl_pem = os.path.join(pubconf.signingroot, "sipl.pem") |
16 | self.sipl_x509 = os.path.join(pubconf.signingroot, "sipl.x509") |
17 | + # Note: the signature tool allows a collection of keys and takes |
18 | + # a directory name with all valid keys. Avoid mixing the |
19 | + # other signing types' keys with the fit keys. |
20 | + self.fit_key = os.path.join(pubconf.signingroot, "fit", "fit.key") |
21 | + self.fit_cert = os.path.join(pubconf.signingroot, "fit", "fit.crt") |
22 | self.autokey = pubconf.signingautokey |
23 | |
24 | self.setComponents(tarfile_path) |
25 | @@ -182,6 +189,8 @@ |
26 | yield (os.path.join(dirpath, filename), self.signOpal) |
27 | elif filename.endswith(".sipl"): |
28 | yield (os.path.join(dirpath, filename), self.signSipl) |
29 | + elif filename.endswith(".fit"): |
30 | + yield (os.path.join(dirpath, filename), self.signFit) |
31 | |
32 | def getKeys(self, which, generate, *keynames): |
33 | """Validate and return the uefi key and cert for encryption.""" |
34 | @@ -213,29 +222,33 @@ |
35 | common_name = "PPA %s %s" % (owner, archive) |
36 | return common_name[0:64 - len(suffix)] + suffix |
37 | |
38 | - def generateUefiKeys(self): |
39 | - """Generate new UEFI Keys for this archive.""" |
40 | - directory = os.path.dirname(self.uefi_key) |
41 | + def generateKeyCrtPair(self, key_type, key_filename, cert_filename): |
42 | + """Generate new Key/Crt key pairs.""" |
43 | + directory = os.path.dirname(key_filename) |
44 | if not os.path.exists(directory): |
45 | os.makedirs(directory) |
46 | |
47 | common_name = self.generateKeyCommonName( |
48 | - self.archive.owner.name, self.archive.name) |
49 | + self.archive.owner.name, self.archive.name, key_type) |
50 | subject = '/CN=' + common_name + '/' |
51 | |
52 | old_mask = os.umask(0o077) |
53 | try: |
54 | new_key_cmd = [ |
55 | 'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048', |
56 | - '-subj', subject, '-keyout', self.uefi_key, |
57 | - '-out', self.uefi_cert, '-days', '3650', '-nodes', '-sha256', |
58 | + '-subj', subject, '-keyout', key_filename, |
59 | + '-out', cert_filename, '-days', '3650', '-nodes', '-sha256', |
60 | ] |
61 | - self.callLog("UEFI keygen", new_key_cmd) |
62 | + self.callLog(key_type + " keygen", new_key_cmd) |
63 | finally: |
64 | os.umask(old_mask) |
65 | |
66 | - if os.path.exists(self.uefi_cert): |
67 | - os.chmod(self.uefi_cert, 0o644) |
68 | + if os.path.exists(cert_filename): |
69 | + os.chmod(cert_filename, 0o644) |
70 | + |
71 | + def generateUefiKeys(self): |
72 | + """Generate new UEFI Keys for this archive.""" |
73 | + self.generateKeyCrtPair("UEFI", self.uefi_key, self.uefi_cert) |
74 | |
75 | def signUefi(self, image): |
76 | """Attempt to sign an image.""" |
77 | @@ -368,6 +381,26 @@ |
78 | cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"] |
79 | return self.callLog("SIPL signing", cmdl) |
80 | |
81 | + def generateFitKeys(self): |
82 | + """Generate new FIT Keys for this archive.""" |
83 | + self.generateKeyCrtPair("FIT", self.fit_key, self.fit_cert) |
84 | + |
85 | + def signFit(self, image): |
86 | + """Attempt to sign an image.""" |
87 | + image_signed = "%s.signed" % image |
88 | + remove_if_exists(image_signed) |
89 | + (key, cert) = self.getKeys('FIT', self.generateFitKeys, |
90 | + self.fit_key, self.fit_cert) |
91 | + if not key or not cert: |
92 | + return |
93 | + self.publishPublicKey(cert) |
94 | + # Make a copy of the image as mkimage signs in place and in |
95 | + # signed-only mode we will remove the original file. |
96 | + shutil.copy(image, image_signed) |
97 | + cmdl = ["mkimage", "-F", "-k", os.path.dirname(key), "-r", |
98 | + image_signed] |
99 | + return self.callLog("FIT signing", cmdl) |
100 | + |
101 | def convertToTarball(self): |
102 | """Convert unpacked output to signing tarball.""" |
103 | tarfilename = os.path.join(self.tmpdir, "signed.tar.gz") |
104 | |
105 | === modified file 'lib/lp/archivepublisher/tests/test_signing.py' |
106 | --- lib/lp/archivepublisher/tests/test_signing.py 2019-06-06 10:57:20 +0000 |
107 | +++ lib/lp/archivepublisher/tests/test_signing.py 2019-06-06 10:57:20 +0000 |
108 | @@ -85,6 +85,8 @@ |
109 | self.callers = { |
110 | "UEFI signing": 0, |
111 | "UEFI keygen": 0, |
112 | + "FIT signing": 0, |
113 | + "FIT keygen": 0, |
114 | "Kmod signing": 0, |
115 | "Kmod keygen key": 0, |
116 | "Kmod keygen cert": 0, |
117 | @@ -111,6 +113,15 @@ |
118 | write_file(self.upload.uefi_key, b"") |
119 | write_file(self.upload.uefi_cert, b"") |
120 | |
121 | + elif description == "FIT signing": |
122 | + filename = cmdl[-1] |
123 | + if filename.endswith(".fit"): |
124 | + write_file(filename + ".signed", b"") |
125 | + |
126 | + elif description == "FIT keygen": |
127 | + write_file(self.upload.fit_key, b"") |
128 | + write_file(self.upload.fit_cert, b"") |
129 | + |
130 | elif description == "Kmod signing": |
131 | filename = cmdl[-1] |
132 | if filename.endswith(".ko.sig"): |
133 | @@ -191,7 +202,7 @@ |
134 | purpose=ArchivePurpose.PPA) |
135 | self.signing_dir = os.path.join( |
136 | self.temp_dir, "signing", "signing-owner", "testing") |
137 | - self.testcase_cn = '/CN=PPA signing-owner testing/' |
138 | + self.testcase_cn = 'PPA signing-owner testing' |
139 | pubconf = getPubConfig(self.archive) |
140 | if not os.path.exists(pubconf.temproot): |
141 | os.makedirs(pubconf.temproot) |
142 | @@ -211,6 +222,15 @@ |
143 | write_file(self.key, b"") |
144 | write_file(self.cert, b"") |
145 | |
146 | + def setUpFitKeys(self, create=True): |
147 | + # We expect and need the fit keys to be in their own |
148 | + # directory as part of key protection for mkimage. |
149 | + self.fit_key = os.path.join(self.signing_dir, "fit", "fit.key") |
150 | + self.fit_cert = os.path.join(self.signing_dir, "fit", "fit.crt") |
151 | + if create: |
152 | + write_file(self.fit_key, b"") |
153 | + write_file(self.fit_cert, b"") |
154 | + |
155 | def setUpKmodKeys(self, create=True): |
156 | self.kmod_pem = os.path.join(self.signing_dir, "kmod.pem") |
157 | self.kmod_x509 = os.path.join(self.signing_dir, "kmod.x509") |
158 | @@ -255,6 +275,7 @@ |
159 | upload = SigningUpload() |
160 | # Under no circumstances is it safe to execute actual commands. |
161 | self.fake_call = FakeMethod(result=0) |
162 | + self.fake_copyfile = FakeMethod(result=0) |
163 | upload.callLog = FakeMethodCallLog(upload=upload) |
164 | self.useFixture(MonkeyPatch("subprocess.call", self.fake_call)) |
165 | upload.process(self.archive, self.path, self.suite) |
166 | @@ -269,6 +290,7 @@ |
167 | upload.signKmod = FakeMethod() |
168 | upload.signOpal = FakeMethod() |
169 | upload.signSipl = FakeMethod() |
170 | + upload.signFit = FakeMethod() |
171 | # Under no circumstances is it safe to execute actual commands. |
172 | fake_call = FakeMethod(result=0) |
173 | self.useFixture(MonkeyPatch("subprocess.call", fake_call)) |
174 | @@ -290,6 +312,7 @@ |
175 | self.tarfile.add_file("1.0/empty.ko", b"") |
176 | self.tarfile.add_file("1.0/empty.opal", b"") |
177 | self.tarfile.add_file("1.0/empty.sipl", b"") |
178 | + self.tarfile.add_file("1.0/empty.fit", b"") |
179 | upload = self.process_emulate() |
180 | self.assertContentEqual([], upload.callLog.caller_list()) |
181 | |
182 | @@ -301,6 +324,7 @@ |
183 | self.tarfile.add_file("1.0/empty.ko", b"") |
184 | self.tarfile.add_file("1.0/empty.opal", b"") |
185 | self.tarfile.add_file("1.0/empty.sipl", b"") |
186 | + self.tarfile.add_file("1.0/empty.fit", b"") |
187 | upload = self.process_emulate() |
188 | self.assertContentEqual([], upload.callLog.caller_list()) |
189 | |
190 | @@ -314,6 +338,7 @@ |
191 | self.tarfile.add_file("1.0/empty.ko", b"") |
192 | self.tarfile.add_file("1.0/empty.opal", b"") |
193 | self.tarfile.add_file("1.0/empty.sipl", b"") |
194 | + self.tarfile.add_file("1.0/empty.fit", b"") |
195 | upload = self.process_emulate() |
196 | expected_callers = [ |
197 | ('UEFI signing', 1), |
198 | @@ -330,6 +355,7 @@ |
199 | self.tarfile.add_file("1.0/empty.ko", b"") |
200 | self.tarfile.add_file("1.0/empty.opal", b"") |
201 | self.tarfile.add_file("1.0/empty.sipl", b"") |
202 | + self.tarfile.add_file("1.0/empty.fit", b"") |
203 | upload = self.process_emulate() |
204 | expected_callers = [ |
205 | ('UEFI keygen', 1), |
206 | @@ -339,10 +365,12 @@ |
207 | ('Opal keygen cert', 1), |
208 | ('SIPL keygen key', 1), |
209 | ('SIPL keygen cert', 1), |
210 | + ('FIT keygen', 1), |
211 | ('UEFI signing', 1), |
212 | ('Kmod signing', 1), |
213 | ('Opal signing', 1), |
214 | ('SIPL signing', 1), |
215 | + ('FIT signing', 1), |
216 | ] |
217 | self.assertContentEqual(expected_callers, upload.callLog.caller_list()) |
218 | |
219 | @@ -417,11 +445,13 @@ |
220 | self.setUpKmodKeys() |
221 | self.setUpOpalKeys() |
222 | self.setUpSiplKeys() |
223 | + self.setUpFitKeys() |
224 | self.openArchive("test", "1.0", "amd64") |
225 | self.tarfile.add_file("1.0/empty.efi", b"") |
226 | self.tarfile.add_file("1.0/empty.ko", b"") |
227 | self.tarfile.add_file("1.0/empty.opal", b"") |
228 | self.tarfile.add_file("1.0/empty.sipl", b"") |
229 | + self.tarfile.add_file("1.0/empty.fit", b"") |
230 | self.process_emulate() |
231 | self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([ |
232 | "1.0/SHA256SUMS", |
233 | @@ -429,6 +459,7 @@ |
234 | "1.0/empty.ko", "1.0/empty.ko.sig", "1.0/control/kmod.x509", |
235 | "1.0/empty.opal", "1.0/empty.opal.sig", "1.0/control/opal.x509", |
236 | "1.0/empty.sipl", "1.0/empty.sipl.sig", "1.0/control/sipl.x509", |
237 | + "1.0/empty.fit", "1.0/empty.fit.signed", "1.0/control/fit.crt", |
238 | ])) |
239 | |
240 | def test_options_tarball(self): |
241 | @@ -438,12 +469,14 @@ |
242 | self.setUpKmodKeys() |
243 | self.setUpOpalKeys() |
244 | self.setUpSiplKeys() |
245 | + self.setUpFitKeys() |
246 | self.openArchive("test", "1.0", "amd64") |
247 | self.tarfile.add_file("1.0/control/options", b"tarball") |
248 | self.tarfile.add_file("1.0/empty.efi", b"") |
249 | self.tarfile.add_file("1.0/empty.ko", b"") |
250 | self.tarfile.add_file("1.0/empty.opal", b"") |
251 | self.tarfile.add_file("1.0/empty.sipl", b"") |
252 | + self.tarfile.add_file("1.0/empty.fit", b"") |
253 | self.process_emulate() |
254 | self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([ |
255 | "1.0/SHA256SUMS", |
256 | @@ -461,6 +494,8 @@ |
257 | '1.0/control/opal.x509', |
258 | '1.0/empty.sipl', '1.0/empty.sipl.sig', |
259 | '1.0/control/sipl.x509', |
260 | + '1.0/empty.fit', '1.0/empty.fit.signed', |
261 | + '1.0/control/fit.crt', |
262 | ], tarball.getnames()) |
263 | |
264 | def test_options_signed_only(self): |
265 | @@ -470,12 +505,14 @@ |
266 | self.setUpKmodKeys() |
267 | self.setUpOpalKeys() |
268 | self.setUpSiplKeys() |
269 | + self.setUpFitKeys() |
270 | self.openArchive("test", "1.0", "amd64") |
271 | self.tarfile.add_file("1.0/control/options", b"signed-only") |
272 | self.tarfile.add_file("1.0/empty.efi", b"") |
273 | self.tarfile.add_file("1.0/empty.ko", b"") |
274 | self.tarfile.add_file("1.0/empty.opal", b"") |
275 | self.tarfile.add_file("1.0/empty.sipl", b"") |
276 | + self.tarfile.add_file("1.0/empty.fit", b"") |
277 | self.process_emulate() |
278 | self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([ |
279 | "1.0/SHA256SUMS", "1.0/control/options", |
280 | @@ -483,6 +520,7 @@ |
281 | "1.0/empty.ko.sig", "1.0/control/kmod.x509", |
282 | "1.0/empty.opal.sig", "1.0/control/opal.x509", |
283 | "1.0/empty.sipl.sig", "1.0/control/sipl.x509", |
284 | + "1.0/empty.fit.signed", "1.0/control/fit.crt", |
285 | ])) |
286 | |
287 | def test_options_tarball_signed_only(self): |
288 | @@ -493,12 +531,14 @@ |
289 | self.setUpKmodKeys() |
290 | self.setUpOpalKeys() |
291 | self.setUpSiplKeys() |
292 | + self.setUpFitKeys() |
293 | self.openArchive("test", "1.0", "amd64") |
294 | self.tarfile.add_file("1.0/control/options", b"tarball\nsigned-only") |
295 | self.tarfile.add_file("1.0/empty.efi", b"") |
296 | self.tarfile.add_file("1.0/empty.ko", b"") |
297 | self.tarfile.add_file("1.0/empty.opal", b"") |
298 | self.tarfile.add_file("1.0/empty.sipl", b"") |
299 | + self.tarfile.add_file("1.0/empty.fit", b"") |
300 | self.process_emulate() |
301 | self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([ |
302 | "1.0/SHA256SUMS", |
303 | @@ -513,12 +553,17 @@ |
304 | '1.0/empty.ko.sig', '1.0/control/kmod.x509', |
305 | '1.0/empty.opal.sig', '1.0/control/opal.x509', |
306 | '1.0/empty.sipl.sig', '1.0/control/sipl.x509', |
307 | + '1.0/empty.fit.signed', '1.0/control/fit.crt', |
308 | ], tarball.getnames()) |
309 | |
310 | def test_no_signed_files(self): |
311 | # Tarballs containing no *.efi files are extracted without complaint. |
312 | # Nothing is signed. |
313 | self.setUpUefiKeys() |
314 | + self.setUpKmodKeys() |
315 | + self.setUpOpalKeys() |
316 | + self.setUpSiplKeys() |
317 | + self.setUpFitKeys() |
318 | self.openArchive("empty", "1.0", "amd64") |
319 | self.tarfile.add_file("1.0/hello", b"world") |
320 | upload = self.process() |
321 | @@ -528,6 +573,7 @@ |
322 | self.assertEqual(0, upload.signKmod.call_count) |
323 | self.assertEqual(0, upload.signOpal.call_count) |
324 | self.assertEqual(0, upload.signSipl.call_count) |
325 | + self.assertEqual(0, upload.signFit.call_count) |
326 | |
327 | def test_already_exists(self): |
328 | # If the target directory already exists, processing fails. |
329 | @@ -595,7 +641,71 @@ |
330 | args = fake_call.calls[0][0][0] |
331 | expected_cmd = [ |
332 | 'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048', |
333 | - '-subj', self.testcase_cn, '-keyout', self.key, '-out', self.cert, |
334 | + '-subj', '/CN=' + self.testcase_cn + ' UEFI/', |
335 | + '-keyout', self.key, '-out', self.cert, |
336 | + '-days', '3650', '-nodes', '-sha256', |
337 | + ] |
338 | + self.assertEqual(expected_cmd, args) |
339 | + |
340 | + def test_correct_fit_signing_command_executed(self): |
341 | + # Check that calling signFit() will generate the expected command |
342 | + # when appropriate keys are present. |
343 | + self.setUpFitKeys() |
344 | + fake_call = FakeMethod(result=0) |
345 | + self.useFixture(MonkeyPatch("subprocess.call", fake_call)) |
346 | + fake_copy = FakeMethod(result=0) |
347 | + self.useFixture(MonkeyPatch("shutil.copy", fake_copy)) |
348 | + upload = SigningUpload() |
349 | + upload.generateFitKeys = FakeMethod() |
350 | + upload.setTargetDirectory( |
351 | + self.archive, "test_1.0_amd64.tar.gz", "distroseries") |
352 | + upload.signFit('t.fit') |
353 | + # Confirm the copy was performed. |
354 | + self.assertEqual(1, fake_copy.call_count) |
355 | + args = fake_copy.calls[0][0] |
356 | + expected_copy = ('t.fit', 't.fit.signed') |
357 | + self.assertEqual(expected_copy, args) |
358 | + # Assert command form. |
359 | + args = fake_call.calls[0][0][0] |
360 | + expected_cmd = [ |
361 | + 'mkimage', '-F', '-k', os.path.dirname(self.fit_key), '-r', |
362 | + 't.fit.signed', |
363 | + ] |
364 | + self.assertEqual(expected_cmd, args) |
365 | + self.assertEqual(0, upload.generateFitKeys.call_count) |
366 | + |
367 | + def test_correct_fit_signing_command_executed_no_keys(self): |
368 | + # Check that calling signFit() will generate no commands when |
369 | + # no keys are present. |
370 | + self.setUpFitKeys(create=False) |
371 | + fake_call = FakeMethod(result=0) |
372 | + self.useFixture(MonkeyPatch("subprocess.call", fake_call)) |
373 | + upload = SigningUpload() |
374 | + upload.generateFitKeys = FakeMethod() |
375 | + upload.setTargetDirectory( |
376 | + self.archive, "test_1.0_amd64.tar.gz", "distroseries") |
377 | + upload.signUefi('t.fit') |
378 | + self.assertEqual(0, fake_call.call_count) |
379 | + self.assertEqual(0, upload.generateFitKeys.call_count) |
380 | + |
381 | + def test_correct_fit_keygen_command_executed(self): |
382 | + # Check that calling generateFitKeys() will generate the |
383 | + # expected command. |
384 | + self.setUpPPA() |
385 | + self.setUpFitKeys(create=False) |
386 | + fake_call = FakeMethod(result=0) |
387 | + self.useFixture(MonkeyPatch("subprocess.call", fake_call)) |
388 | + upload = SigningUpload() |
389 | + upload.setTargetDirectory( |
390 | + self.archive, "test_1.0_amd64.tar.gz", "distroseries") |
391 | + upload.generateFitKeys() |
392 | + self.assertEqual(1, fake_call.call_count) |
393 | + # Assert the actual command matches. |
394 | + args = fake_call.calls[0][0][0] |
395 | + expected_cmd = [ |
396 | + 'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048', |
397 | + '-subj', '/CN=' + self.testcase_cn + ' FIT/', |
398 | + '-keyout', self.fit_key, '-out', self.fit_cert, |
399 | '-days', '3650', '-nodes', '-sha256', |
400 | ] |
401 | self.assertEqual(expected_cmd, args) |
402 | @@ -610,7 +720,7 @@ |
403 | text = upload.generateOpensslConfig('Kmod', upload.openssl_config_kmod) |
404 | |
405 | id_re = re.compile(r'^# KMOD OpenSSL config\n') |
406 | - cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn[4:-1] + '\s+Kmod') |
407 | + cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn + '\s+Kmod') |
408 | eku_re = re.compile( |
409 | r'\bextendedKeyUsage\s*=\s*' |
410 | r'codeSigning,1.3.6.1.4.1.2312.16.1.2\s*\b') |
411 | @@ -696,7 +806,7 @@ |
412 | text = upload.generateOpensslConfig('Opal', upload.openssl_config_opal) |
413 | |
414 | id_re = re.compile(r'^# OPAL OpenSSL config\n') |
415 | - cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn[4:-1] + '\s+Opal') |
416 | + cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn + '\s+Opal') |
417 | |
418 | self.assertIn('[ req ]', text) |
419 | self.assertIsNotNone(id_re.search(text)) |
420 | @@ -779,7 +889,7 @@ |
421 | text = upload.generateOpensslConfig('SIPL', upload.openssl_config_sipl) |
422 | |
423 | id_re = re.compile(r'^# SIPL OpenSSL config\n') |
424 | - cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn[4:-1] + '\s+SIPL') |
425 | + cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn + '\s+SIPL') |
426 | |
427 | self.assertIn('[ req ]', text) |
428 | self.assertIsNotNone(id_re.search(text)) |
429 | @@ -860,6 +970,14 @@ |
430 | upload = self.process() |
431 | self.assertEqual(1, upload.signUefi.call_count) |
432 | |
433 | + def test_signs_fit_image(self): |
434 | + # Each image in the tarball is signed. |
435 | + self.setUpFitKeys() |
436 | + self.openArchive("test", "1.0", "amd64") |
437 | + self.tarfile.add_file("1.0/empty.fit", b"") |
438 | + upload = self.process() |
439 | + self.assertEqual(1, upload.signFit.call_count) |
440 | + |
441 | def test_signs_kmod_image(self): |
442 | # Each image in the tarball is signed. |
443 | self.setUpKmodKeys() |
444 | @@ -886,7 +1004,6 @@ |
445 | |
446 | def test_signs_combo_image(self): |
447 | # Each image in the tarball is signed. |
448 | - self.setUpKmodKeys() |
449 | self.openArchive("test", "1.0", "amd64") |
450 | self.tarfile.add_file("1.0/empty.efi", b"") |
451 | self.tarfile.add_file("1.0/empty.ko", b"") |
452 | @@ -898,11 +1015,17 @@ |
453 | self.tarfile.add_file("1.0/empty2.sipl", b"") |
454 | self.tarfile.add_file("1.0/empty3.sipl", b"") |
455 | self.tarfile.add_file("1.0/empty4.sipl", b"") |
456 | + self.tarfile.add_file("1.0/empty.fit", b"") |
457 | + self.tarfile.add_file("1.0/empty2.fit", b"") |
458 | + self.tarfile.add_file("1.0/empty3.fit", b"") |
459 | + self.tarfile.add_file("1.0/empty4.fit", b"") |
460 | + self.tarfile.add_file("1.0/empty5.fit", b"") |
461 | upload = self.process() |
462 | self.assertEqual(1, upload.signUefi.call_count) |
463 | self.assertEqual(2, upload.signKmod.call_count) |
464 | self.assertEqual(3, upload.signOpal.call_count) |
465 | self.assertEqual(4, upload.signSipl.call_count) |
466 | + self.assertEqual(5, upload.signFit.call_count) |
467 | |
468 | def test_installed(self): |
469 | # Files in the tarball are installed correctly. |
470 | @@ -974,6 +1097,43 @@ |
471 | self.assertEqual(stat.S_IMODE(os.stat(self.key).st_mode), 0o600) |
472 | self.assertEqual(stat.S_IMODE(os.stat(self.cert).st_mode), 0o644) |
473 | |
474 | + def test_create_fit_keys_autokey_off(self): |
475 | + # Keys are not created. |
476 | + self.setUpFitKeys(create=False) |
477 | + self.assertFalse(os.path.exists(self.fit_key)) |
478 | + self.assertFalse(os.path.exists(self.fit_cert)) |
479 | + fake_call = FakeMethod(result=0) |
480 | + self.useFixture(MonkeyPatch("subprocess.call", fake_call)) |
481 | + upload = SigningUpload() |
482 | + upload.callLog = FakeMethodCallLog(upload=upload) |
483 | + upload.setTargetDirectory( |
484 | + self.archive, "test_1.0_amd64.tar.gz", "distroseries") |
485 | + upload.signFit(os.path.join(self.makeTemporaryDirectory(), 'fit')) |
486 | + self.assertEqual(0, upload.callLog.caller_count('FIT keygen')) |
487 | + self.assertFalse(os.path.exists(self.fit_key)) |
488 | + self.assertFalse(os.path.exists(self.fit_cert)) |
489 | + |
490 | + def test_create_fit_keys_autokey_on(self): |
491 | + # Keys are created on demand. |
492 | + self.setUpPPA() |
493 | + self.setUpFitKeys(create=False) |
494 | + self.assertFalse(os.path.exists(self.fit_key)) |
495 | + self.assertFalse(os.path.exists(self.fit_cert)) |
496 | + fake_call = FakeMethod(result=0) |
497 | + self.useFixture(MonkeyPatch("subprocess.call", fake_call)) |
498 | + fake_copy = FakeMethod(result=0) |
499 | + self.useFixture(MonkeyPatch("shutil.copy", fake_copy)) |
500 | + upload = SigningUpload() |
501 | + upload.callLog = FakeMethodCallLog(upload=upload) |
502 | + upload.setTargetDirectory( |
503 | + self.archive, "test_1.0_amd64.tar.gz", "distroseries") |
504 | + upload.signFit(os.path.join(self.makeTemporaryDirectory(), 't.fit')) |
505 | + self.assertEqual(1, upload.callLog.caller_count('FIT keygen')) |
506 | + self.assertTrue(os.path.exists(self.fit_key)) |
507 | + self.assertTrue(os.path.exists(self.fit_cert)) |
508 | + self.assertEqual(stat.S_IMODE(os.stat(self.fit_key).st_mode), 0o600) |
509 | + self.assertEqual(stat.S_IMODE(os.stat(self.fit_cert).st_mode), 0o644) |
510 | + |
511 | def test_create_kmod_keys_autokey_off(self): |
512 | # Keys are not created. |
513 | self.setUpKmodKeys(create=False) |
This looks OK to me, thanks.
Prerequisites for landing this MP:
* u-boot security updates must be landed in xenial-updates and bionic-updates (Andy is on top of this) soyuz-dependenc ies 0.135 must be installed on pepo and haetae (I'll deal with this)
* launchpad-