Merge lp:~apw/launchpad/signing-sipl into lp:launchpad

Proposed by Andy Whitcroft
Status: Merged
Merged at revision: 18979
Proposed branch: lp:~apw/launchpad/signing-sipl
Merge into: lp:launchpad
Diff against target: 575 lines (+237/-23)
2 files modified
lib/lp/archivepublisher/signing.py (+39/-17)
lib/lp/archivepublisher/tests/test_signing.py (+198/-6)
To merge this branch: bzr merge lp:~apw/launchpad/signing-sipl
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+368275@code.launchpad.net

Commit message

Add s390x Secure Initial Program Load signing support.

Description of the change

On newer s390x mainframes zipl implements signature verification for zipl stage 3 and for the loaded kernel binaries. These signatures are essentially kernel module signing signatures. Add support for performing SIPL signing against *.sipl files in the signing custom uploads.

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

Looks pretty much OK; just a few nits.

review: Approve
Revision history for this message
Andy Whitcroft (apw) wrote :

Hopefully that is all of the nits addressed.

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 2018-08-03 16:10:41 +0000
+++ lib/lp/archivepublisher/signing.py 2019-06-06 09:45:01 +0000
@@ -95,6 +95,8 @@
95 self.kmod_x509 = None95 self.kmod_x509 = None
96 self.opal_pem = None96 self.opal_pem = None
97 self.opal_x509 = None97 self.opal_x509 = None
98 self.sipl_pem = None
99 self.sipl_x509 = None
98 self.autokey = False100 self.autokey = False
99 else:101 else:
100 self.uefi_key = os.path.join(pubconf.signingroot, "uefi.key")102 self.uefi_key = os.path.join(pubconf.signingroot, "uefi.key")
@@ -103,6 +105,8 @@
103 self.kmod_x509 = os.path.join(pubconf.signingroot, "kmod.x509")105 self.kmod_x509 = os.path.join(pubconf.signingroot, "kmod.x509")
104 self.opal_pem = os.path.join(pubconf.signingroot, "opal.pem")106 self.opal_pem = os.path.join(pubconf.signingroot, "opal.pem")
105 self.opal_x509 = os.path.join(pubconf.signingroot, "opal.x509")107 self.opal_x509 = os.path.join(pubconf.signingroot, "opal.x509")
108 self.sipl_pem = os.path.join(pubconf.signingroot, "sipl.pem")
109 self.sipl_x509 = os.path.join(pubconf.signingroot, "sipl.x509")
106 self.autokey = pubconf.signingautokey110 self.autokey = pubconf.signingautokey
107111
108 self.setComponents(tarfile_path)112 self.setComponents(tarfile_path)
@@ -176,6 +180,8 @@
176 yield (os.path.join(dirpath, filename), self.signKmod)180 yield (os.path.join(dirpath, filename), self.signKmod)
177 elif filename.endswith(".opal"):181 elif filename.endswith(".opal"):
178 yield (os.path.join(dirpath, filename), self.signOpal)182 yield (os.path.join(dirpath, filename), self.signOpal)
183 elif filename.endswith(".sipl"):
184 yield (os.path.join(dirpath, filename), self.signSipl)
179185
180 def getKeys(self, which, generate, *keynames):186 def getKeys(self, which, generate, *keynames):
181 """Validate and return the uefi key and cert for encryption."""187 """Validate and return the uefi key and cert for encryption."""
@@ -242,7 +248,7 @@
242 cmdl = ["sbsign", "--key", key, "--cert", cert, image]248 cmdl = ["sbsign", "--key", key, "--cert", cert, image]
243 return self.callLog("UEFI signing", cmdl)249 return self.callLog("UEFI signing", cmdl)
244250
245 openssl_config_opal = textwrap.dedent("""251 openssl_config_base = textwrap.dedent("""\
246 [ req ]252 [ req ]
247 default_bits = 4096253 default_bits = 4096
248 distinguished_name = req_distinguished_name254 distinguished_name = req_distinguished_name
@@ -260,37 +266,35 @@
260 authorityKeyIdentifier=keyid266 authorityKeyIdentifier=keyid
261 """)267 """)
262268
263 openssl_config_kmod = openssl_config_opal + textwrap.dedent("""269 openssl_config_opal = "# OPAL OpenSSL config\n" + openssl_config_base
270
271 openssl_config_kmod = "# KMOD OpenSSL config\n" + openssl_config_base + \
272 textwrap.dedent("""
264 # codeSigning: specifies that this key is used to sign code.273 # codeSigning: specifies that this key is used to sign code.
265 # 1.3.6.1.4.1.2312.16.1.2: defines this key as used for274 # 1.3.6.1.4.1.2312.16.1.2: defines this key as used for
266 # module signing only. See https://lkml.org/lkml/2015/8/26/741.275 # module signing only. See https://lkml.org/lkml/2015/8/26/741.
267 extendedKeyUsage = codeSigning,1.3.6.1.4.1.2312.16.1.2276 extendedKeyUsage = codeSigning,1.3.6.1.4.1.2312.16.1.2
268 """)277 """)
269278
270 def generateOpensslConfig(self, key_type, common_name):279 openssl_config_sipl = "# SIPL OpenSSL config\n" + openssl_config_base
271 if key_type == 'Kmod':280
272 genkey_tmpl = self.openssl_config_kmod281 def generateOpensslConfig(self, key_type, genkey_tmpl):
273 elif key_type == 'Opal':282 # Truncate name to 64 character maximum.
274 genkey_tmpl = self.openssl_config_opal283 common_name = self.generateKeyCommonName(
275 else:284 self.archive.owner.name, self.archive.name, key_type)
276 raise ValueError("unknown key_type " + key_type)
277285
278 return genkey_tmpl.format(common_name=common_name)286 return genkey_tmpl.format(common_name=common_name)
279287
280 def generatePemX509Pair(self, key_type, pem_filename, x509_filename):288 def generatePemX509Pair(self, key_type, genkey_text, pem_filename,
289 x509_filename):
281 """Generate new pem/x509 key pairs."""290 """Generate new pem/x509 key pairs."""
282 directory = os.path.dirname(pem_filename)291 directory = os.path.dirname(pem_filename)
283 if not os.path.exists(directory):292 if not os.path.exists(directory):
284 os.makedirs(directory)293 os.makedirs(directory)
285294
286 # Truncate name to 64 character maximum.
287 common_name = self.generateKeyCommonName(
288 self.archive.owner.name, self.archive.name, key_type)
289
290 old_mask = os.umask(0o077)295 old_mask = os.umask(0o077)
291 try:296 try:
292 with tempfile.NamedTemporaryFile(suffix='.keygen') as tf:297 with tempfile.NamedTemporaryFile(suffix='.keygen') as tf:
293 genkey_text = self.generateOpensslConfig(key_type, common_name)
294 print(genkey_text, file=tf)298 print(genkey_text, file=tf)
295299
296 # Close out the underlying file so we know it is complete.300 # Close out the underlying file so we know it is complete.
@@ -318,7 +322,8 @@
318322
319 def generateKmodKeys(self):323 def generateKmodKeys(self):
320 """Generate new Kernel Signing Keys for this archive."""324 """Generate new Kernel Signing Keys for this archive."""
321 self.generatePemX509Pair("Kmod", self.kmod_pem, self.kmod_x509)325 config = self.generateOpensslConfig("Kmod", self.openssl_config_kmod)
326 self.generatePemX509Pair("Kmod", config, self.kmod_pem, self.kmod_x509)
322327
323 def signKmod(self, image):328 def signKmod(self, image):
324 """Attempt to sign a kernel module."""329 """Attempt to sign a kernel module."""
@@ -333,7 +338,8 @@
333338
334 def generateOpalKeys(self):339 def generateOpalKeys(self):
335 """Generate new Opal Signing Keys for this archive."""340 """Generate new Opal Signing Keys for this archive."""
336 self.generatePemX509Pair("Opal", self.opal_pem, self.opal_x509)341 config = self.generateOpensslConfig("Opal", self.openssl_config_opal)
342 self.generatePemX509Pair("Opal", config, self.opal_pem, self.opal_x509)
337343
338 def signOpal(self, image):344 def signOpal(self, image):
339 """Attempt to sign a kernel image for Opal."""345 """Attempt to sign a kernel image for Opal."""
@@ -346,6 +352,22 @@
346 cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]352 cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
347 return self.callLog("Opal signing", cmdl)353 return self.callLog("Opal signing", cmdl)
348354
355 def generateSiplKeys(self):
356 """Generate new Sipl Signing Keys for this archive."""
357 config = self.generateOpensslConfig("SIPL", self.openssl_config_sipl)
358 self.generatePemX509Pair("SIPL", config, self.sipl_pem, self.sipl_x509)
359
360 def signSipl(self, image):
361 """Attempt to sign a kernel image for Sipl."""
362 remove_if_exists("%s.sig" % image)
363 (pem, cert) = self.getKeys('SIPL Kernel', self.generateSiplKeys,
364 self.sipl_pem, self.sipl_x509)
365 if not pem or not cert:
366 return
367 self.publishPublicKey(cert)
368 cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
369 return self.callLog("SIPL signing", cmdl)
370
349 def convertToTarball(self):371 def convertToTarball(self):
350 """Convert unpacked output to signing tarball."""372 """Convert unpacked output to signing tarball."""
351 tarfilename = os.path.join(self.tmpdir, "signed.tar.gz")373 tarfilename = os.path.join(self.tmpdir, "signed.tar.gz")
352374
=== modified file 'lib/lp/archivepublisher/tests/test_signing.py'
--- lib/lp/archivepublisher/tests/test_signing.py 2019-05-24 11:10:38 +0000
+++ lib/lp/archivepublisher/tests/test_signing.py 2019-06-06 09:45:01 +0000
@@ -91,6 +91,9 @@
91 "Opal signing": 0,91 "Opal signing": 0,
92 "Opal keygen key": 0,92 "Opal keygen key": 0,
93 "Opal keygen cert": 0,93 "Opal keygen cert": 0,
94 "SIPL signing": 0,
95 "SIPL keygen key": 0,
96 "SIPL keygen cert": 0,
94 }97 }
9598
96 def __call__(self, *args, **kwargs):99 def __call__(self, *args, **kwargs):
@@ -130,9 +133,20 @@
130 elif description == "Opal keygen key":133 elif description == "Opal keygen key":
131 write_file(self.upload.opal_pem, b"")134 write_file(self.upload.opal_pem, b"")
132135
136 elif description == "SIPL signing":
137 filename = cmdl[-1]
138 if filename.endswith(".sipl.sig"):
139 write_file(filename, b"")
140
141 elif description == "SIPL keygen cert":
142 write_file(self.upload.sipl_x509, b"")
143
144 elif description == "SIPL keygen key":
145 write_file(self.upload.sipl_pem, b"")
146
133 else:147 else:
134 raise AssertionError("unknown command executed cmd=(%s)" %148 raise AssertionError("unknown command executed description=(%s) "
135 " ".join(cmdl))149 "cmd=(%s)" % (description, " ".join(cmdl)))
136150
137 return 0151 return 0
138152
@@ -211,6 +225,13 @@
211 write_file(self.opal_pem, b"")225 write_file(self.opal_pem, b"")
212 write_file(self.opal_x509, b"")226 write_file(self.opal_x509, b"")
213227
228 def setUpSiplKeys(self, create=True):
229 self.sipl_pem = os.path.join(self.signing_dir, "sipl.pem")
230 self.sipl_x509 = os.path.join(self.signing_dir, "sipl.x509")
231 if create:
232 write_file(self.sipl_pem, b"")
233 write_file(self.sipl_x509, b"")
234
214 def openArchive(self, loader_type, version, arch):235 def openArchive(self, loader_type, version, arch):
215 self.path = os.path.join(236 self.path = os.path.join(
216 self.temp_dir, "%s_%s_%s.tar.gz" % (loader_type, version, arch))237 self.temp_dir, "%s_%s_%s.tar.gz" % (loader_type, version, arch))
@@ -247,6 +268,7 @@
247 upload.signUefi = FakeMethod()268 upload.signUefi = FakeMethod()
248 upload.signKmod = FakeMethod()269 upload.signKmod = FakeMethod()
249 upload.signOpal = FakeMethod()270 upload.signOpal = FakeMethod()
271 upload.signSipl = FakeMethod()
250 # Under no circumstances is it safe to execute actual commands.272 # Under no circumstances is it safe to execute actual commands.
251 fake_call = FakeMethod(result=0)273 fake_call = FakeMethod(result=0)
252 self.useFixture(MonkeyPatch("subprocess.call", fake_call))274 self.useFixture(MonkeyPatch("subprocess.call", fake_call))
@@ -267,6 +289,7 @@
267 self.tarfile.add_file("1.0/empty.efi", b"")289 self.tarfile.add_file("1.0/empty.efi", b"")
268 self.tarfile.add_file("1.0/empty.ko", b"")290 self.tarfile.add_file("1.0/empty.ko", b"")
269 self.tarfile.add_file("1.0/empty.opal", b"")291 self.tarfile.add_file("1.0/empty.opal", b"")
292 self.tarfile.add_file("1.0/empty.sipl", b"")
270 upload = self.process_emulate()293 upload = self.process_emulate()
271 self.assertContentEqual([], upload.callLog.caller_list())294 self.assertContentEqual([], upload.callLog.caller_list())
272295
@@ -277,6 +300,7 @@
277 self.tarfile.add_file("1.0/empty.efi", b"")300 self.tarfile.add_file("1.0/empty.efi", b"")
278 self.tarfile.add_file("1.0/empty.ko", b"")301 self.tarfile.add_file("1.0/empty.ko", b"")
279 self.tarfile.add_file("1.0/empty.opal", b"")302 self.tarfile.add_file("1.0/empty.opal", b"")
303 self.tarfile.add_file("1.0/empty.sipl", b"")
280 upload = self.process_emulate()304 upload = self.process_emulate()
281 self.assertContentEqual([], upload.callLog.caller_list())305 self.assertContentEqual([], upload.callLog.caller_list())
282306
@@ -289,6 +313,7 @@
289 self.tarfile.add_file("1.0/empty.efi", b"")313 self.tarfile.add_file("1.0/empty.efi", b"")
290 self.tarfile.add_file("1.0/empty.ko", b"")314 self.tarfile.add_file("1.0/empty.ko", b"")
291 self.tarfile.add_file("1.0/empty.opal", b"")315 self.tarfile.add_file("1.0/empty.opal", b"")
316 self.tarfile.add_file("1.0/empty.sipl", b"")
292 upload = self.process_emulate()317 upload = self.process_emulate()
293 expected_callers = [318 expected_callers = [
294 ('UEFI signing', 1),319 ('UEFI signing', 1),
@@ -304,6 +329,7 @@
304 self.tarfile.add_file("1.0/empty.efi", b"")329 self.tarfile.add_file("1.0/empty.efi", b"")
305 self.tarfile.add_file("1.0/empty.ko", b"")330 self.tarfile.add_file("1.0/empty.ko", b"")
306 self.tarfile.add_file("1.0/empty.opal", b"")331 self.tarfile.add_file("1.0/empty.opal", b"")
332 self.tarfile.add_file("1.0/empty.sipl", b"")
307 upload = self.process_emulate()333 upload = self.process_emulate()
308 expected_callers = [334 expected_callers = [
309 ('UEFI keygen', 1),335 ('UEFI keygen', 1),
@@ -311,9 +337,12 @@
311 ('Kmod keygen cert', 1),337 ('Kmod keygen cert', 1),
312 ('Opal keygen key', 1),338 ('Opal keygen key', 1),
313 ('Opal keygen cert', 1),339 ('Opal keygen cert', 1),
340 ('SIPL keygen key', 1),
341 ('SIPL keygen cert', 1),
314 ('UEFI signing', 1),342 ('UEFI signing', 1),
315 ('Kmod signing', 1),343 ('Kmod signing', 1),
316 ('Opal signing', 1),344 ('Opal signing', 1),
345 ('SIPL signing', 1),
317 ]346 ]
318 self.assertContentEqual(expected_callers, upload.callLog.caller_list())347 self.assertContentEqual(expected_callers, upload.callLog.caller_list())
319348
@@ -387,16 +416,19 @@
387 self.setUpUefiKeys()416 self.setUpUefiKeys()
388 self.setUpKmodKeys()417 self.setUpKmodKeys()
389 self.setUpOpalKeys()418 self.setUpOpalKeys()
419 self.setUpSiplKeys()
390 self.openArchive("test", "1.0", "amd64")420 self.openArchive("test", "1.0", "amd64")
391 self.tarfile.add_file("1.0/empty.efi", b"")421 self.tarfile.add_file("1.0/empty.efi", b"")
392 self.tarfile.add_file("1.0/empty.ko", b"")422 self.tarfile.add_file("1.0/empty.ko", b"")
393 self.tarfile.add_file("1.0/empty.opal", b"")423 self.tarfile.add_file("1.0/empty.opal", b"")
424 self.tarfile.add_file("1.0/empty.sipl", b"")
394 self.process_emulate()425 self.process_emulate()
395 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([426 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
396 "1.0/SHA256SUMS",427 "1.0/SHA256SUMS",
397 "1.0/empty.efi", "1.0/empty.efi.signed", "1.0/control/uefi.crt",428 "1.0/empty.efi", "1.0/empty.efi.signed", "1.0/control/uefi.crt",
398 "1.0/empty.ko", "1.0/empty.ko.sig", "1.0/control/kmod.x509",429 "1.0/empty.ko", "1.0/empty.ko.sig", "1.0/control/kmod.x509",
399 "1.0/empty.opal", "1.0/empty.opal.sig", "1.0/control/opal.x509",430 "1.0/empty.opal", "1.0/empty.opal.sig", "1.0/control/opal.x509",
431 "1.0/empty.sipl", "1.0/empty.sipl.sig", "1.0/control/sipl.x509",
400 ]))432 ]))
401433
402 def test_options_tarball(self):434 def test_options_tarball(self):
@@ -405,11 +437,13 @@
405 self.setUpUefiKeys()437 self.setUpUefiKeys()
406 self.setUpKmodKeys()438 self.setUpKmodKeys()
407 self.setUpOpalKeys()439 self.setUpOpalKeys()
440 self.setUpSiplKeys()
408 self.openArchive("test", "1.0", "amd64")441 self.openArchive("test", "1.0", "amd64")
409 self.tarfile.add_file("1.0/control/options", b"tarball")442 self.tarfile.add_file("1.0/control/options", b"tarball")
410 self.tarfile.add_file("1.0/empty.efi", b"")443 self.tarfile.add_file("1.0/empty.efi", b"")
411 self.tarfile.add_file("1.0/empty.ko", b"")444 self.tarfile.add_file("1.0/empty.ko", b"")
412 self.tarfile.add_file("1.0/empty.opal", b"")445 self.tarfile.add_file("1.0/empty.opal", b"")
446 self.tarfile.add_file("1.0/empty.sipl", b"")
413 self.process_emulate()447 self.process_emulate()
414 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([448 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
415 "1.0/SHA256SUMS",449 "1.0/SHA256SUMS",
@@ -425,6 +459,8 @@
425 '1.0/empty.ko', '1.0/empty.ko.sig', '1.0/control/kmod.x509',459 '1.0/empty.ko', '1.0/empty.ko.sig', '1.0/control/kmod.x509',
426 '1.0/empty.opal', '1.0/empty.opal.sig',460 '1.0/empty.opal', '1.0/empty.opal.sig',
427 '1.0/control/opal.x509',461 '1.0/control/opal.x509',
462 '1.0/empty.sipl', '1.0/empty.sipl.sig',
463 '1.0/control/sipl.x509',
428 ], tarball.getnames())464 ], tarball.getnames())
429465
430 def test_options_signed_only(self):466 def test_options_signed_only(self):
@@ -433,17 +469,20 @@
433 self.setUpUefiKeys()469 self.setUpUefiKeys()
434 self.setUpKmodKeys()470 self.setUpKmodKeys()
435 self.setUpOpalKeys()471 self.setUpOpalKeys()
472 self.setUpSiplKeys()
436 self.openArchive("test", "1.0", "amd64")473 self.openArchive("test", "1.0", "amd64")
437 self.tarfile.add_file("1.0/control/options", b"signed-only")474 self.tarfile.add_file("1.0/control/options", b"signed-only")
438 self.tarfile.add_file("1.0/empty.efi", b"")475 self.tarfile.add_file("1.0/empty.efi", b"")
439 self.tarfile.add_file("1.0/empty.ko", b"")476 self.tarfile.add_file("1.0/empty.ko", b"")
440 self.tarfile.add_file("1.0/empty.opal", b"")477 self.tarfile.add_file("1.0/empty.opal", b"")
478 self.tarfile.add_file("1.0/empty.sipl", b"")
441 self.process_emulate()479 self.process_emulate()
442 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([480 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
443 "1.0/SHA256SUMS", "1.0/control/options",481 "1.0/SHA256SUMS", "1.0/control/options",
444 "1.0/empty.efi.signed", "1.0/control/uefi.crt",482 "1.0/empty.efi.signed", "1.0/control/uefi.crt",
445 "1.0/empty.ko.sig", "1.0/control/kmod.x509",483 "1.0/empty.ko.sig", "1.0/control/kmod.x509",
446 "1.0/empty.opal.sig", "1.0/control/opal.x509",484 "1.0/empty.opal.sig", "1.0/control/opal.x509",
485 "1.0/empty.sipl.sig", "1.0/control/sipl.x509",
447 ]))486 ]))
448487
449 def test_options_tarball_signed_only(self):488 def test_options_tarball_signed_only(self):
@@ -453,11 +492,13 @@
453 self.setUpUefiKeys()492 self.setUpUefiKeys()
454 self.setUpKmodKeys()493 self.setUpKmodKeys()
455 self.setUpOpalKeys()494 self.setUpOpalKeys()
495 self.setUpSiplKeys()
456 self.openArchive("test", "1.0", "amd64")496 self.openArchive("test", "1.0", "amd64")
457 self.tarfile.add_file("1.0/control/options", b"tarball\nsigned-only")497 self.tarfile.add_file("1.0/control/options", b"tarball\nsigned-only")
458 self.tarfile.add_file("1.0/empty.efi", b"")498 self.tarfile.add_file("1.0/empty.efi", b"")
459 self.tarfile.add_file("1.0/empty.ko", b"")499 self.tarfile.add_file("1.0/empty.ko", b"")
460 self.tarfile.add_file("1.0/empty.opal", b"")500 self.tarfile.add_file("1.0/empty.opal", b"")
501 self.tarfile.add_file("1.0/empty.sipl", b"")
461 self.process_emulate()502 self.process_emulate()
462 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([503 self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
463 "1.0/SHA256SUMS",504 "1.0/SHA256SUMS",
@@ -471,6 +512,7 @@
471 '1.0/empty.efi.signed', '1.0/control/uefi.crt',512 '1.0/empty.efi.signed', '1.0/control/uefi.crt',
472 '1.0/empty.ko.sig', '1.0/control/kmod.x509',513 '1.0/empty.ko.sig', '1.0/control/kmod.x509',
473 '1.0/empty.opal.sig', '1.0/control/opal.x509',514 '1.0/empty.opal.sig', '1.0/control/opal.x509',
515 '1.0/empty.sipl.sig', '1.0/control/sipl.x509',
474 ], tarball.getnames())516 ], tarball.getnames())
475517
476 def test_no_signed_files(self):518 def test_no_signed_files(self):
@@ -484,6 +526,8 @@
484 self.getSignedPath("empty", "amd64"), "1.0", "hello")))526 self.getSignedPath("empty", "amd64"), "1.0", "hello")))
485 self.assertEqual(0, upload.signUefi.call_count)527 self.assertEqual(0, upload.signUefi.call_count)
486 self.assertEqual(0, upload.signKmod.call_count)528 self.assertEqual(0, upload.signKmod.call_count)
529 self.assertEqual(0, upload.signOpal.call_count)
530 self.assertEqual(0, upload.signSipl.call_count)
487531
488 def test_already_exists(self):532 def test_already_exists(self):
489 # If the target directory already exists, processing fails.533 # If the target directory already exists, processing fails.
@@ -559,15 +603,20 @@
559 def test_correct_kmod_openssl_config(self):603 def test_correct_kmod_openssl_config(self):
560 # Check that calling generateOpensslConfig() will return an appropriate604 # Check that calling generateOpensslConfig() will return an appropriate
561 # openssl configuration.605 # openssl configuration.
606 self.setUpPPA()
562 upload = SigningUpload()607 upload = SigningUpload()
563 text = upload.generateOpensslConfig('Kmod', 'something-unique')608 upload.setTargetDirectory(
609 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
610 text = upload.generateOpensslConfig('Kmod', upload.openssl_config_kmod)
564611
565 cn_re = re.compile(r'\bCN\s*=\s*something-unique\b')612 id_re = re.compile(r'^# KMOD OpenSSL config\n')
613 cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn[4:-1] + '\s+Kmod')
566 eku_re = re.compile(614 eku_re = re.compile(
567 r'\bextendedKeyUsage\s*=\s*'615 r'\bextendedKeyUsage\s*=\s*'
568 r'codeSigning,1.3.6.1.4.1.2312.16.1.2\s*\b')616 r'codeSigning,1.3.6.1.4.1.2312.16.1.2\s*\b')
569617
570 self.assertIn('[ req ]', text)618 self.assertIn('[ req ]', text)
619 self.assertIsNotNone(id_re.search(text))
571 self.assertIsNotNone(cn_re.search(text))620 self.assertIsNotNone(cn_re.search(text))
572 self.assertIsNotNone(eku_re.search(text))621 self.assertIsNotNone(eku_re.search(text))
573622
@@ -640,12 +689,17 @@
640 def test_correct_opal_openssl_config(self):689 def test_correct_opal_openssl_config(self):
641 # Check that calling generateOpensslConfig() will return an appropriate690 # Check that calling generateOpensslConfig() will return an appropriate
642 # openssl configuration.691 # openssl configuration.
692 self.setUpPPA()
643 upload = SigningUpload()693 upload = SigningUpload()
644 text = upload.generateOpensslConfig('Opal', 'something-unique')694 upload.setTargetDirectory(
695 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
696 text = upload.generateOpensslConfig('Opal', upload.openssl_config_opal)
645697
646 cn_re = re.compile(r'\bCN\s*=\s*something-unique\b')698 id_re = re.compile(r'^# OPAL OpenSSL config\n')
699 cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn[4:-1] + '\s+Opal')
647700
648 self.assertIn('[ req ]', text)701 self.assertIn('[ req ]', text)
702 self.assertIsNotNone(id_re.search(text))
649 self.assertIsNotNone(cn_re.search(text))703 self.assertIsNotNone(cn_re.search(text))
650 self.assertNotIn('extendedKeyUsage', text)704 self.assertNotIn('extendedKeyUsage', text)
651705
@@ -715,6 +769,89 @@
715 ]769 ]
716 self.assertEqual(expected_cmd, args)770 self.assertEqual(expected_cmd, args)
717771
772 def test_correct_sipl_openssl_config(self):
773 # Check that calling generateOpensslConfig() will return an appropriate
774 # openssl configuration.
775 self.setUpPPA()
776 upload = SigningUpload()
777 upload.setTargetDirectory(
778 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
779 text = upload.generateOpensslConfig('SIPL', upload.openssl_config_sipl)
780
781 id_re = re.compile(r'^# SIPL OpenSSL config\n')
782 cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn[4:-1] + '\s+SIPL')
783
784 self.assertIn('[ req ]', text)
785 self.assertIsNotNone(id_re.search(text))
786 self.assertIsNotNone(cn_re.search(text))
787 self.assertNotIn('extendedKeyUsage', text)
788
789 def test_correct_sipl_signing_command_executed(self):
790 # Check that calling signSipl() will generate the expected command
791 # when appropriate keys are present.
792 self.setUpSiplKeys()
793 fake_call = FakeMethod(result=0)
794 self.useFixture(MonkeyPatch("subprocess.call", fake_call))
795 upload = SigningUpload()
796 upload.generateSiplKeys = FakeMethod()
797 upload.setTargetDirectory(
798 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
799 upload.signSipl('t.sipl')
800 self.assertEqual(1, fake_call.call_count)
801 # Assert command form.
802 args = fake_call.calls[0][0][0]
803 expected_cmd = [
804 'kmodsign', '-D', 'sha512', self.sipl_pem, self.sipl_x509,
805 't.sipl', 't.sipl.sig'
806 ]
807 self.assertEqual(expected_cmd, args)
808 self.assertEqual(0, upload.generateSiplKeys.call_count)
809
810 def test_correct_sipl_signing_command_executed_no_keys(self):
811 # Check that calling signSipl() will generate no commands when
812 # no keys are present.
813 self.setUpSiplKeys(create=False)
814 fake_call = FakeMethod(result=0)
815 self.useFixture(MonkeyPatch("subprocess.call", fake_call))
816 upload = SigningUpload()
817 upload.generateSiplKeys = FakeMethod()
818 upload.setTargetDirectory(
819 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
820 upload.signOpal('t.sipl')
821 self.assertEqual(0, fake_call.call_count)
822 self.assertEqual(0, upload.generateSiplKeys.call_count)
823
824 def test_correct_sipl_keygen_command_executed(self):
825 # Check that calling generateSiplKeys() will generate the
826 # expected command.
827 self.setUpPPA()
828 self.setUpSiplKeys(create=False)
829 fake_call = FakeMethod(result=0)
830 self.useFixture(MonkeyPatch("subprocess.call", fake_call))
831 upload = SigningUpload()
832 upload.setTargetDirectory(
833 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
834 upload.generateSiplKeys()
835 self.assertEqual(2, fake_call.call_count)
836 # Assert the actual command matches.
837 args = fake_call.calls[0][0][0]
838 # Sanitise the keygen tmp file.
839 if args[11].endswith('.keygen'):
840 args[11] = 'XXX.keygen'
841 expected_cmd = [
842 'openssl', 'req', '-new', '-nodes', '-utf8', '-sha512',
843 '-days', '3650', '-batch', '-x509',
844 '-config', 'XXX.keygen', '-outform', 'PEM',
845 '-out', self.sipl_pem, '-keyout', self.sipl_pem
846 ]
847 self.assertEqual(expected_cmd, args)
848 args = fake_call.calls[1][0][0]
849 expected_cmd = [
850 'openssl', 'x509', '-in', self.sipl_pem, '-outform', 'DER',
851 '-out', self.sipl_x509
852 ]
853 self.assertEqual(expected_cmd, args)
854
718 def test_signs_uefi_image(self):855 def test_signs_uefi_image(self):
719 # Each image in the tarball is signed.856 # Each image in the tarball is signed.
720 self.setUpUefiKeys()857 self.setUpUefiKeys()
@@ -739,6 +876,14 @@
739 upload = self.process()876 upload = self.process()
740 self.assertEqual(1, upload.signOpal.call_count)877 self.assertEqual(1, upload.signOpal.call_count)
741878
879 def test_signs_sipl_image(self):
880 # Each image in the tarball is signed.
881 self.setUpSiplKeys()
882 self.openArchive("test", "1.0", "amd64")
883 self.tarfile.add_file("1.0/empty.sipl", b"")
884 upload = self.process()
885 self.assertEqual(1, upload.signSipl.call_count)
886
742 def test_signs_combo_image(self):887 def test_signs_combo_image(self):
743 # Each image in the tarball is signed.888 # Each image in the tarball is signed.
744 self.setUpKmodKeys()889 self.setUpKmodKeys()
@@ -749,10 +894,15 @@
749 self.tarfile.add_file("1.0/empty.opal", b"")894 self.tarfile.add_file("1.0/empty.opal", b"")
750 self.tarfile.add_file("1.0/empty2.opal", b"")895 self.tarfile.add_file("1.0/empty2.opal", b"")
751 self.tarfile.add_file("1.0/empty3.opal", b"")896 self.tarfile.add_file("1.0/empty3.opal", b"")
897 self.tarfile.add_file("1.0/empty.sipl", b"")
898 self.tarfile.add_file("1.0/empty2.sipl", b"")
899 self.tarfile.add_file("1.0/empty3.sipl", b"")
900 self.tarfile.add_file("1.0/empty4.sipl", b"")
752 upload = self.process()901 upload = self.process()
753 self.assertEqual(1, upload.signUefi.call_count)902 self.assertEqual(1, upload.signUefi.call_count)
754 self.assertEqual(2, upload.signKmod.call_count)903 self.assertEqual(2, upload.signKmod.call_count)
755 self.assertEqual(3, upload.signOpal.call_count)904 self.assertEqual(3, upload.signOpal.call_count)
905 self.assertEqual(4, upload.signSipl.call_count)
756906
757 def test_installed(self):907 def test_installed(self):
758 # Files in the tarball are installed correctly.908 # Files in the tarball are installed correctly.
@@ -898,16 +1048,55 @@
898 self.assertEqual(stat.S_IMODE(os.stat(self.opal_pem).st_mode), 0o600)1048 self.assertEqual(stat.S_IMODE(os.stat(self.opal_pem).st_mode), 0o600)
899 self.assertEqual(stat.S_IMODE(os.stat(self.opal_x509).st_mode), 0o644)1049 self.assertEqual(stat.S_IMODE(os.stat(self.opal_x509).st_mode), 0o644)
9001050
1051 def test_create_sipl_keys_autokey_off(self):
1052 # Keys are not created.
1053 self.setUpSiplKeys(create=False)
1054 self.assertFalse(os.path.exists(self.sipl_pem))
1055 self.assertFalse(os.path.exists(self.sipl_x509))
1056 fake_call = FakeMethod(result=0)
1057 self.useFixture(MonkeyPatch("subprocess.call", fake_call))
1058 upload = SigningUpload()
1059 upload.callLog = FakeMethodCallLog(upload=upload)
1060 upload.setTargetDirectory(
1061 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
1062 upload.signOpal(os.path.join(self.makeTemporaryDirectory(), 't.sipl'))
1063 self.assertEqual(0, upload.callLog.caller_count('SIPL keygen key'))
1064 self.assertEqual(0, upload.callLog.caller_count('SIPL keygen cert'))
1065 self.assertFalse(os.path.exists(self.sipl_pem))
1066 self.assertFalse(os.path.exists(self.sipl_x509))
1067
1068 def test_create_sipl_keys_autokey_on(self):
1069 # Keys are created on demand.
1070 self.setUpPPA()
1071 self.setUpSiplKeys(create=False)
1072 self.assertFalse(os.path.exists(self.sipl_pem))
1073 self.assertFalse(os.path.exists(self.sipl_x509))
1074 fake_call = FakeMethod(result=0)
1075 self.useFixture(MonkeyPatch("subprocess.call", fake_call))
1076 upload = SigningUpload()
1077 upload.callLog = FakeMethodCallLog(upload=upload)
1078 upload.setTargetDirectory(
1079 self.archive, "test_1.0_amd64.tar.gz", "distroseries")
1080 upload.signSipl(os.path.join(self.makeTemporaryDirectory(), 't.sipl'))
1081 self.assertEqual(1, upload.callLog.caller_count('SIPL keygen key'))
1082 self.assertEqual(1, upload.callLog.caller_count('SIPL keygen cert'))
1083 self.assertTrue(os.path.exists(self.sipl_pem))
1084 self.assertTrue(os.path.exists(self.sipl_x509))
1085 self.assertEqual(stat.S_IMODE(os.stat(self.sipl_pem).st_mode), 0o600)
1086 self.assertEqual(stat.S_IMODE(os.stat(self.sipl_x509).st_mode), 0o644)
1087
901 def test_checksumming_tree(self):1088 def test_checksumming_tree(self):
902 # Specifying no options should leave us with an open tree,1089 # Specifying no options should leave us with an open tree,
903 # confirm it is checksummed.1090 # confirm it is checksummed.
904 self.setUpUefiKeys()1091 self.setUpUefiKeys()
905 self.setUpKmodKeys()1092 self.setUpKmodKeys()
906 self.setUpOpalKeys()1093 self.setUpOpalKeys()
1094 self.setUpSiplKeys()
907 self.openArchive("test", "1.0", "amd64")1095 self.openArchive("test", "1.0", "amd64")
908 self.tarfile.add_file("1.0/empty.efi", b"")1096 self.tarfile.add_file("1.0/empty.efi", b"")
909 self.tarfile.add_file("1.0/empty.ko", b"")1097 self.tarfile.add_file("1.0/empty.ko", b"")
910 self.tarfile.add_file("1.0/empty.opal", b"")1098 self.tarfile.add_file("1.0/empty.opal", b"")
1099 self.tarfile.add_file("1.0/empty.sipl", b"")
911 self.process_emulate()1100 self.process_emulate()
912 sha256file = os.path.join(self.getSignedPath("test", "amd64"),1101 sha256file = os.path.join(self.getSignedPath("test", "amd64"),
913 "1.0", "SHA256SUMS")1102 "1.0", "SHA256SUMS")
@@ -926,6 +1115,7 @@
926 self.tarfile.add_file("1.0/empty.efi", b"")1115 self.tarfile.add_file("1.0/empty.efi", b"")
927 self.tarfile.add_file("1.0/empty.ko", b"")1116 self.tarfile.add_file("1.0/empty.ko", b"")
928 self.tarfile.add_file("1.0/empty.opal", b"")1117 self.tarfile.add_file("1.0/empty.opal", b"")
1118 self.tarfile.add_file("1.0/empty.sipl", b"")
929 self.process_emulate()1119 self.process_emulate()
930 sha256file = os.path.join(self.getSignedPath("test", "amd64"),1120 sha256file = os.path.join(self.getSignedPath("test", "amd64"),
931 "1.0", "SHA256SUMS")1121 "1.0", "SHA256SUMS")
@@ -949,6 +1139,7 @@
949 self.tarfile.add_file("1.0/empty.efi", b"")1139 self.tarfile.add_file("1.0/empty.efi", b"")
950 self.tarfile.add_file("1.0/empty.ko", b"")1140 self.tarfile.add_file("1.0/empty.ko", b"")
951 self.tarfile.add_file("1.0/empty.opal", b"")1141 self.tarfile.add_file("1.0/empty.opal", b"")
1142 self.tarfile.add_file("1.0/empty.sipl", b"")
952 self.process_emulate()1143 self.process_emulate()
953 sha256file = os.path.join(self.getSignedPath("test", "amd64"),1144 sha256file = os.path.join(self.getSignedPath("test", "amd64"),
954 "1.0", "SHA256SUMS")1145 "1.0", "SHA256SUMS")
@@ -982,6 +1173,7 @@
982 self.tarfile.add_file("1.0/empty.efi", "")1173 self.tarfile.add_file("1.0/empty.efi", "")
983 self.tarfile.add_file("1.0/empty.ko", "")1174 self.tarfile.add_file("1.0/empty.ko", "")
984 self.tarfile.add_file("1.0/empty.opal", "")1175 self.tarfile.add_file("1.0/empty.opal", "")
1176 self.tarfile.add_file("1.0/empty.sipl", "")
985 self.process_emulate()1177 self.process_emulate()
986 sha256file = os.path.join(self.getSignedPath("test", "amd64"),1178 sha256file = os.path.join(self.getSignedPath("test", "amd64"),
987 "1.0", "SHA256SUMS")1179 "1.0", "SHA256SUMS")