launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #23697
[Merge] lp:~apw/launchpad/signing-fit into lp:launchpad
Andy Whitcroft has proposed merging lp:~apw/launchpad/signing-fit into lp:launchpad.
Commit message:
Add u-boot Flat Image Tree signing support. 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.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~apw/launchpad/signing-fit/+merge/368276
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~apw/launchpad/signing-fit into lp:launchpad.
=== 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-03 14:59:55 +0000
@@ -95,6 +95,10 @@
self.kmod_x509 = None
self.opal_pem = None
self.opal_x509 = None
+ self.sipl_pem = None
+ self.sipl_x509 = None
+ self.fit_key = None
+ self.fit_cert = None
self.autokey = False
else:
self.uefi_key = os.path.join(pubconf.signingroot, "uefi.key")
@@ -103,6 +107,13 @@
self.kmod_x509 = os.path.join(pubconf.signingroot, "kmod.x509")
self.opal_pem = os.path.join(pubconf.signingroot, "opal.pem")
self.opal_x509 = os.path.join(pubconf.signingroot, "opal.x509")
+ self.sipl_pem = os.path.join(pubconf.signingroot, "sipl.pem")
+ self.sipl_x509 = os.path.join(pubconf.signingroot, "sipl.x509")
+ # Note: the signature tool allows a collection of keys and takes
+ # a directory name with all valid keys. Avoid mixing the
+ # other signing types' keys with the fit keys.
+ self.fit_key = os.path.join(pubconf.signingroot, "fit", "fit.key")
+ self.fit_cert = os.path.join(pubconf.signingroot, "fit", "fit.crt")
self.autokey = pubconf.signingautokey
self.setComponents(tarfile_path)
@@ -176,6 +187,10 @@
yield (os.path.join(dirpath, filename), self.signKmod)
elif filename.endswith(".opal"):
yield (os.path.join(dirpath, filename), self.signOpal)
+ elif filename.endswith(".sipl"):
+ yield (os.path.join(dirpath, filename), self.signSipl)
+ elif filename.endswith(".fit"):
+ yield (os.path.join(dirpath, filename), self.signFit)
def getKeys(self, which, generate, *keynames):
"""Validate and return the uefi key and cert for encryption."""
@@ -207,29 +222,33 @@
common_name = "PPA %s %s" % (owner, archive)
return common_name[0:64 - len(suffix)] + suffix
- def generateUefiKeys(self):
- """Generate new UEFI Keys for this archive."""
- directory = os.path.dirname(self.uefi_key)
+ def generateKeyCrtPair(self, key_type, key_filename, cert_filename):
+ """Generate new Key/Crt key pairs."""
+ directory = os.path.dirname(key_filename)
if not os.path.exists(directory):
os.makedirs(directory)
common_name = self.generateKeyCommonName(
- self.archive.owner.name, self.archive.name)
+ self.archive.owner.name, self.archive.name, key_type)
subject = '/CN=' + common_name + '/'
old_mask = os.umask(0o077)
try:
new_key_cmd = [
'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048',
- '-subj', subject, '-keyout', self.uefi_key,
- '-out', self.uefi_cert, '-days', '3650', '-nodes', '-sha256',
+ '-subj', subject, '-keyout', key_filename,
+ '-out', cert_filename, '-days', '3650', '-nodes', '-sha256',
]
- self.callLog("UEFI keygen", new_key_cmd)
+ self.callLog(key_type + " keygen", new_key_cmd)
finally:
os.umask(old_mask)
- if os.path.exists(self.uefi_cert):
- os.chmod(self.uefi_cert, 0o644)
+ if os.path.exists(cert_filename):
+ os.chmod(cert_filename, 0o644)
+
+ def generateUefiKeys(self):
+ """Generate new UEFI Keys for this archive."""
+ self.generateKeyCrtPair("UEFI", self.uefi_key, self.uefi_cert)
def signUefi(self, image):
"""Attempt to sign an image."""
@@ -242,7 +261,7 @@
cmdl = ["sbsign", "--key", key, "--cert", cert, image]
return self.callLog("UEFI signing", cmdl)
- openssl_config_opal = textwrap.dedent("""
+ openssl_config_base = textwrap.dedent("""
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
@@ -260,37 +279,35 @@
authorityKeyIdentifier=keyid
""")
- openssl_config_kmod = openssl_config_opal + textwrap.dedent("""
+ openssl_config_opal = "# OPAL openssl config" + openssl_config_base
+
+ openssl_config_kmod = "# KMOD openssl config" + openssl_config_base + \
+ textwrap.dedent("""
# codeSigning: specifies that this key is used to sign code.
# 1.3.6.1.4.1.2312.16.1.2: defines this key as used for
# module signing only. See https://lkml.org/lkml/2015/8/26/741.
extendedKeyUsage = codeSigning,1.3.6.1.4.1.2312.16.1.2
""")
- def generateOpensslConfig(self, key_type, common_name):
- if key_type == 'Kmod':
- genkey_tmpl = self.openssl_config_kmod
- elif key_type == 'Opal':
- genkey_tmpl = self.openssl_config_opal
- else:
- raise ValueError("unknown key_type " + key_type)
+ openssl_config_sipl = "# SIPL openssl config" + openssl_config_base
+
+ def generateOpensslConfig(self, key_type, genkey_tmpl):
+ # Truncate name to 64 character maximum.
+ common_name = self.generateKeyCommonName(
+ self.archive.owner.name, self.archive.name, key_type)
return genkey_tmpl.format(common_name=common_name)
- def generatePemX509Pair(self, key_type, pem_filename, x509_filename):
+ def generatePemX509Pair(self, key_type, genkey_text, pem_filename,
+ x509_filename):
"""Generate new pem/x509 key pairs."""
directory = os.path.dirname(pem_filename)
if not os.path.exists(directory):
os.makedirs(directory)
- # Truncate name to 64 character maximum.
- common_name = self.generateKeyCommonName(
- self.archive.owner.name, self.archive.name, key_type)
-
old_mask = os.umask(0o077)
try:
with tempfile.NamedTemporaryFile(suffix='.keygen') as tf:
- genkey_text = self.generateOpensslConfig(key_type, common_name)
print(genkey_text, file=tf)
# Close out the underlying file so we know it is complete.
@@ -318,7 +335,8 @@
def generateKmodKeys(self):
"""Generate new Kernel Signing Keys for this archive."""
- self.generatePemX509Pair("Kmod", self.kmod_pem, self.kmod_x509)
+ config = self.generateOpensslConfig("Kmod", self.openssl_config_kmod)
+ self.generatePemX509Pair("Kmod", config, self.kmod_pem, self.kmod_x509)
def signKmod(self, image):
"""Attempt to sign a kernel module."""
@@ -333,7 +351,8 @@
def generateOpalKeys(self):
"""Generate new Opal Signing Keys for this archive."""
- self.generatePemX509Pair("Opal", self.opal_pem, self.opal_x509)
+ config = self.generateOpensslConfig("Opal", self.openssl_config_opal)
+ self.generatePemX509Pair("Opal", config, self.opal_pem, self.opal_x509)
def signOpal(self, image):
"""Attempt to sign a kernel image for Opal."""
@@ -346,6 +365,42 @@
cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
return self.callLog("Opal signing", cmdl)
+ def generateSiplKeys(self):
+ """Generate new Sipl Signing Keys for this archive."""
+ config = self.generateOpensslConfig("Sipl", self.openssl_config_sipl)
+ self.generatePemX509Pair("Sipl", config, self.sipl_pem, self.sipl_x509)
+
+ def signSipl(self, image):
+ """Attempt to sign a kernel image for Sipl."""
+ remove_if_exists("%s.sig" % image)
+ (pem, cert) = self.getKeys('Sipl Kernel', self.generateSiplKeys,
+ self.sipl_pem, self.sipl_x509)
+ if not pem or not cert:
+ return
+ self.publishPublicKey(cert)
+ cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
+ return self.callLog("Sipl signing", cmdl)
+
+ def generateFitKeys(self):
+ """Generate new Fit Keys for this archive."""
+ self.generateKeyCrtPair("Fit", self.fit_key, self.fit_cert)
+
+ def signFit(self, image):
+ """Attempt to sign an image."""
+ image_signed = "%s.signed" % image
+ remove_if_exists(image_signed)
+ (key, cert) = self.getKeys('Fit', self.generateFitKeys,
+ self.fit_key, self.fit_cert)
+ if not key or not cert:
+ return
+ self.publishPublicKey(cert)
+ # Make a copy of the image as mkimage signs in place and in
+ # signed-only mode we will remove the original file.
+ shutil.copy(image, image_signed)
+ cmdl = ["mkimage", "-F", "-k", os.path.dirname(key), "-r",
+ image_signed]
+ return self.callLog("Fit signing", cmdl)
+
def convertToTarball(self):
"""Convert unpacked output to signing tarball."""
tarfilename = os.path.join(self.tmpdir, "signed.tar.gz")
=== 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-03 14:59:55 +0000
@@ -85,12 +85,17 @@
self.callers = {
"UEFI signing": 0,
"UEFI keygen": 0,
+ "Fit signing": 0,
+ "Fit keygen": 0,
"Kmod signing": 0,
"Kmod keygen key": 0,
"Kmod keygen cert": 0,
"Opal signing": 0,
"Opal keygen key": 0,
"Opal keygen cert": 0,
+ "Sipl signing": 0,
+ "Sipl keygen key": 0,
+ "Sipl keygen cert": 0,
}
def __call__(self, *args, **kwargs):
@@ -108,6 +113,15 @@
write_file(self.upload.uefi_key, b"")
write_file(self.upload.uefi_cert, b"")
+ elif description == "Fit signing":
+ filename = cmdl[-1]
+ if filename.endswith(".fit"):
+ write_file(filename + ".signed", b"")
+
+ elif description == "Fit keygen":
+ write_file(self.upload.fit_key, b"")
+ write_file(self.upload.fit_cert, b"")
+
elif description == "Kmod signing":
filename = cmdl[-1]
if filename.endswith(".ko.sig"):
@@ -130,9 +144,20 @@
elif description == "Opal keygen key":
write_file(self.upload.opal_pem, b"")
+ elif description == "Sipl signing":
+ filename = cmdl[-1]
+ if filename.endswith(".sipl.sig"):
+ write_file(filename, b"")
+
+ elif description == "Sipl keygen cert":
+ write_file(self.upload.sipl_x509, b"")
+
+ elif description == "Sipl keygen key":
+ write_file(self.upload.sipl_pem, b"")
+
else:
- raise AssertionError("unknown command executed cmd=(%s)" %
- " ".join(cmdl))
+ raise AssertionError("unknown command executed description=(%s) "
+ "cmd=(%s)" % (description, " ".join(cmdl)))
return 0
@@ -177,7 +202,7 @@
purpose=ArchivePurpose.PPA)
self.signing_dir = os.path.join(
self.temp_dir, "signing", "signing-owner", "testing")
- self.testcase_cn = '/CN=PPA signing-owner testing/'
+ self.testcase_cn = 'PPA signing-owner testing'
pubconf = getPubConfig(self.archive)
if not os.path.exists(pubconf.temproot):
os.makedirs(pubconf.temproot)
@@ -197,6 +222,15 @@
write_file(self.key, b"")
write_file(self.cert, b"")
+ def setUpFitKeys(self, create=True):
+ # We expect and need the fit keys to be in their own
+ # directory as part of key protection for mkimage.
+ self.fit_key = os.path.join(self.signing_dir, "fit", "fit.key")
+ self.fit_cert = os.path.join(self.signing_dir, "fit", "fit.crt")
+ if create:
+ write_file(self.fit_key, b"")
+ write_file(self.fit_cert, b"")
+
def setUpKmodKeys(self, create=True):
self.kmod_pem = os.path.join(self.signing_dir, "kmod.pem")
self.kmod_x509 = os.path.join(self.signing_dir, "kmod.x509")
@@ -211,6 +245,13 @@
write_file(self.opal_pem, b"")
write_file(self.opal_x509, b"")
+ def setUpSiplKeys(self, create=True):
+ self.sipl_pem = os.path.join(self.signing_dir, "sipl.pem")
+ self.sipl_x509 = os.path.join(self.signing_dir, "sipl.x509")
+ if create:
+ write_file(self.sipl_pem, b"")
+ write_file(self.sipl_x509, b"")
+
def openArchive(self, loader_type, version, arch):
self.path = os.path.join(
self.temp_dir, "%s_%s_%s.tar.gz" % (loader_type, version, arch))
@@ -234,6 +275,7 @@
upload = SigningUpload()
# Under no circumstances is it safe to execute actual commands.
self.fake_call = FakeMethod(result=0)
+ self.fake_copyfile = FakeMethod(result=0)
upload.callLog = FakeMethodCallLog(upload=upload)
self.useFixture(MonkeyPatch("subprocess.call", self.fake_call))
upload.process(self.archive, self.path, self.suite)
@@ -247,6 +289,8 @@
upload.signUefi = FakeMethod()
upload.signKmod = FakeMethod()
upload.signOpal = FakeMethod()
+ upload.signSipl = FakeMethod()
+ upload.signFit = FakeMethod()
# Under no circumstances is it safe to execute actual commands.
fake_call = FakeMethod(result=0)
self.useFixture(MonkeyPatch("subprocess.call", fake_call))
@@ -267,6 +311,8 @@
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
upload = self.process_emulate()
self.assertContentEqual([], upload.callLog.caller_list())
@@ -277,6 +323,8 @@
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
upload = self.process_emulate()
self.assertContentEqual([], upload.callLog.caller_list())
@@ -289,6 +337,8 @@
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
upload = self.process_emulate()
expected_callers = [
('UEFI signing', 1),
@@ -304,6 +354,8 @@
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
upload = self.process_emulate()
expected_callers = [
('UEFI keygen', 1),
@@ -311,9 +363,14 @@
('Kmod keygen cert', 1),
('Opal keygen key', 1),
('Opal keygen cert', 1),
+ ('Sipl keygen key', 1),
+ ('Sipl keygen cert', 1),
+ ('Fit keygen', 1),
('UEFI signing', 1),
('Kmod signing', 1),
('Opal signing', 1),
+ ('Sipl signing', 1),
+ ('Fit signing', 1),
]
self.assertContentEqual(expected_callers, upload.callLog.caller_list())
@@ -387,16 +444,22 @@
self.setUpUefiKeys()
self.setUpKmodKeys()
self.setUpOpalKeys()
+ self.setUpSiplKeys()
+ self.setUpFitKeys()
self.openArchive("test", "1.0", "amd64")
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
self.process_emulate()
self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
"1.0/SHA256SUMS",
"1.0/empty.efi", "1.0/empty.efi.signed", "1.0/control/uefi.crt",
"1.0/empty.ko", "1.0/empty.ko.sig", "1.0/control/kmod.x509",
"1.0/empty.opal", "1.0/empty.opal.sig", "1.0/control/opal.x509",
+ "1.0/empty.sipl", "1.0/empty.sipl.sig", "1.0/control/sipl.x509",
+ "1.0/empty.fit", "1.0/empty.fit.signed", "1.0/control/fit.crt",
]))
def test_options_tarball(self):
@@ -405,11 +468,15 @@
self.setUpUefiKeys()
self.setUpKmodKeys()
self.setUpOpalKeys()
+ self.setUpSiplKeys()
+ self.setUpFitKeys()
self.openArchive("test", "1.0", "amd64")
self.tarfile.add_file("1.0/control/options", b"tarball")
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
self.process_emulate()
self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
"1.0/SHA256SUMS",
@@ -425,6 +492,10 @@
'1.0/empty.ko', '1.0/empty.ko.sig', '1.0/control/kmod.x509',
'1.0/empty.opal', '1.0/empty.opal.sig',
'1.0/control/opal.x509',
+ '1.0/empty.sipl', '1.0/empty.sipl.sig',
+ '1.0/control/sipl.x509',
+ '1.0/empty.fit', '1.0/empty.fit.signed',
+ '1.0/control/fit.crt',
], tarball.getnames())
def test_options_signed_only(self):
@@ -433,17 +504,23 @@
self.setUpUefiKeys()
self.setUpKmodKeys()
self.setUpOpalKeys()
+ self.setUpSiplKeys()
+ self.setUpFitKeys()
self.openArchive("test", "1.0", "amd64")
self.tarfile.add_file("1.0/control/options", b"signed-only")
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
self.process_emulate()
self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
"1.0/SHA256SUMS", "1.0/control/options",
"1.0/empty.efi.signed", "1.0/control/uefi.crt",
"1.0/empty.ko.sig", "1.0/control/kmod.x509",
"1.0/empty.opal.sig", "1.0/control/opal.x509",
+ "1.0/empty.sipl.sig", "1.0/control/sipl.x509",
+ "1.0/empty.fit.signed", "1.0/control/fit.crt",
]))
def test_options_tarball_signed_only(self):
@@ -453,11 +530,15 @@
self.setUpUefiKeys()
self.setUpKmodKeys()
self.setUpOpalKeys()
+ self.setUpSiplKeys()
+ self.setUpFitKeys()
self.openArchive("test", "1.0", "amd64")
self.tarfile.add_file("1.0/control/options", b"tarball\nsigned-only")
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
self.process_emulate()
self.assertThat(self.getSignedPath("test", "amd64"), SignedMatches([
"1.0/SHA256SUMS",
@@ -471,12 +552,18 @@
'1.0/empty.efi.signed', '1.0/control/uefi.crt',
'1.0/empty.ko.sig', '1.0/control/kmod.x509',
'1.0/empty.opal.sig', '1.0/control/opal.x509',
+ '1.0/empty.sipl.sig', '1.0/control/sipl.x509',
+ '1.0/empty.fit.signed', '1.0/control/fit.crt',
], tarball.getnames())
def test_no_signed_files(self):
# Tarballs containing no *.efi files are extracted without complaint.
# Nothing is signed.
self.setUpUefiKeys()
+ self.setUpKmodKeys()
+ self.setUpOpalKeys()
+ self.setUpSiplKeys()
+ self.setUpFitKeys()
self.openArchive("empty", "1.0", "amd64")
self.tarfile.add_file("1.0/hello", b"world")
upload = self.process()
@@ -484,6 +571,9 @@
self.getSignedPath("empty", "amd64"), "1.0", "hello")))
self.assertEqual(0, upload.signUefi.call_count)
self.assertEqual(0, upload.signKmod.call_count)
+ self.assertEqual(0, upload.signOpal.call_count)
+ self.assertEqual(0, upload.signSipl.call_count)
+ self.assertEqual(0, upload.signFit.call_count)
def test_already_exists(self):
# If the target directory already exists, processing fails.
@@ -551,7 +641,71 @@
args = fake_call.calls[0][0][0]
expected_cmd = [
'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048',
- '-subj', self.testcase_cn, '-keyout', self.key, '-out', self.cert,
+ '-subj', '/CN=' + self.testcase_cn + ' UEFI/',
+ '-keyout', self.key, '-out', self.cert,
+ '-days', '3650', '-nodes', '-sha256',
+ ]
+ self.assertEqual(expected_cmd, args)
+
+ def test_correct_fit_signing_command_executed(self):
+ # Check that calling signFit() will generate the expected command
+ # when appropriate keys are present.
+ self.setUpFitKeys()
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ fake_copy = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("shutil.copy", fake_copy))
+ upload = SigningUpload()
+ upload.generateFitKeys = FakeMethod()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signFit('t.fit')
+ # Confirm the copy was performed.
+ self.assertEqual(1, fake_copy.call_count)
+ args = fake_copy.calls[0][0]
+ expected_copy = ('t.fit', 't.fit.signed')
+ self.assertEqual(expected_copy, args)
+ # Assert command form.
+ args = fake_call.calls[0][0][0]
+ expected_cmd = [
+ 'mkimage', '-F', '-k', os.path.dirname(self.fit_key), '-r',
+ 't.fit.signed',
+ ]
+ self.assertEqual(expected_cmd, args)
+ self.assertEqual(0, upload.generateFitKeys.call_count)
+
+ def test_correct_fit_signing_command_executed_no_keys(self):
+ # Check that calling signFit() will generate no commands when
+ # no keys are present.
+ self.setUpFitKeys(create=False)
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.generateFitKeys = FakeMethod()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signUefi('t.fit')
+ self.assertEqual(0, fake_call.call_count)
+ self.assertEqual(0, upload.generateFitKeys.call_count)
+
+ def test_correct_fit_keygen_command_executed(self):
+ # Check that calling generateFitKeys() will generate the
+ # expected command.
+ self.setUpPPA()
+ self.setUpFitKeys(create=False)
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.generateFitKeys()
+ self.assertEqual(1, fake_call.call_count)
+ # Assert the actual command matches.
+ args = fake_call.calls[0][0][0]
+ expected_cmd = [
+ 'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048',
+ '-subj', '/CN=' + self.testcase_cn + ' Fit/',
+ '-keyout', self.fit_key, '-out', self.fit_cert,
'-days', '3650', '-nodes', '-sha256',
]
self.assertEqual(expected_cmd, args)
@@ -559,15 +713,20 @@
def test_correct_kmod_openssl_config(self):
# Check that calling generateOpensslConfig() will return an appropriate
# openssl configuration.
+ self.setUpPPA()
upload = SigningUpload()
- text = upload.generateOpensslConfig('Kmod', 'something-unique')
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ text = upload.generateOpensslConfig('Kmod', upload.openssl_config_kmod)
- cn_re = re.compile(r'\bCN\s*=\s*something-unique\b')
+ id_re = re.compile(r'^# KMOD openssl config\b')
+ cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn + '\s+Kmod')
eku_re = re.compile(
r'\bextendedKeyUsage\s*=\s*'
r'codeSigning,1.3.6.1.4.1.2312.16.1.2\s*\b')
self.assertIn('[ req ]', text)
+ self.assertIsNotNone(id_re.search(text))
self.assertIsNotNone(cn_re.search(text))
self.assertIsNotNone(eku_re.search(text))
@@ -640,12 +799,17 @@
def test_correct_opal_openssl_config(self):
# Check that calling generateOpensslConfig() will return an appropriate
# openssl configuration.
+ self.setUpPPA()
upload = SigningUpload()
- text = upload.generateOpensslConfig('Opal', 'something-unique')
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ text = upload.generateOpensslConfig('Opal', upload.openssl_config_opal)
- cn_re = re.compile(r'\bCN\s*=\s*something-unique\b')
+ id_re = re.compile(r'^# OPAL openssl config\b')
+ cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn + '\s+Opal')
self.assertIn('[ req ]', text)
+ self.assertIsNotNone(id_re.search(text))
self.assertIsNotNone(cn_re.search(text))
self.assertNotIn('extendedKeyUsage', text)
@@ -715,6 +879,89 @@
]
self.assertEqual(expected_cmd, args)
+ def test_correct_sipl_openssl_config(self):
+ # Check that calling generateOpensslConfig() will return an appropriate
+ # openssl configuration.
+ self.setUpPPA()
+ upload = SigningUpload()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ text = upload.generateOpensslConfig('Sipl', upload.openssl_config_sipl)
+
+ id_re = re.compile(r'^# SIPL openssl config\b')
+ cn_re = re.compile(r'\bCN\s*=\s*' + self.testcase_cn + '\s+Sipl')
+
+ self.assertIn('[ req ]', text)
+ self.assertIsNotNone(id_re.search(text))
+ self.assertIsNotNone(cn_re.search(text))
+ self.assertNotIn('extendedKeyUsage', text)
+
+ def test_correct_sipl_signing_command_executed(self):
+ # Check that calling signSipl() will generate the expected command
+ # when appropriate keys are present.
+ self.setUpSiplKeys()
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.generateSiplKeys = FakeMethod()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signSipl('t.sipl')
+ self.assertEqual(1, fake_call.call_count)
+ # Assert command form.
+ args = fake_call.calls[0][0][0]
+ expected_cmd = [
+ 'kmodsign', '-D', 'sha512', self.sipl_pem, self.sipl_x509,
+ 't.sipl', 't.sipl.sig'
+ ]
+ self.assertEqual(expected_cmd, args)
+ self.assertEqual(0, upload.generateSiplKeys.call_count)
+
+ def test_correct_sipl_signing_command_executed_no_keys(self):
+ # Check that calling signSipl() will generate no commands when
+ # no keys are present.
+ self.setUpSiplKeys(create=False)
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.generateSiplKeys = FakeMethod()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signOpal('t.sipl')
+ self.assertEqual(0, fake_call.call_count)
+ self.assertEqual(0, upload.generateSiplKeys.call_count)
+
+ def test_correct_sipl_keygen_command_executed(self):
+ # Check that calling generateSiplKeys() will generate the
+ # expected command.
+ self.setUpPPA()
+ self.setUpSiplKeys(create=False)
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.generateSiplKeys()
+ self.assertEqual(2, fake_call.call_count)
+ # Assert the actual command matches.
+ args = fake_call.calls[0][0][0]
+ # Sanitise the keygen tmp file.
+ if args[11].endswith('.keygen'):
+ args[11] = 'XXX.keygen'
+ expected_cmd = [
+ 'openssl', 'req', '-new', '-nodes', '-utf8', '-sha512',
+ '-days', '3650', '-batch', '-x509',
+ '-config', 'XXX.keygen', '-outform', 'PEM',
+ '-out', self.sipl_pem, '-keyout', self.sipl_pem
+ ]
+ self.assertEqual(expected_cmd, args)
+ args = fake_call.calls[1][0][0]
+ expected_cmd = [
+ 'openssl', 'x509', '-in', self.sipl_pem, '-outform', 'DER',
+ '-out', self.sipl_x509
+ ]
+ self.assertEqual(expected_cmd, args)
+
def test_signs_uefi_image(self):
# Each image in the tarball is signed.
self.setUpUefiKeys()
@@ -723,6 +970,14 @@
upload = self.process()
self.assertEqual(1, upload.signUefi.call_count)
+ def test_signs_fit_image(self):
+ # Each image in the tarball is signed.
+ self.setUpFitKeys()
+ self.openArchive("test", "1.0", "amd64")
+ self.tarfile.add_file("1.0/empty.fit", b"")
+ upload = self.process()
+ self.assertEqual(1, upload.signFit.call_count)
+
def test_signs_kmod_image(self):
# Each image in the tarball is signed.
self.setUpKmodKeys()
@@ -739,9 +994,16 @@
upload = self.process()
self.assertEqual(1, upload.signOpal.call_count)
+ def test_signs_sipl_image(self):
+ # Each image in the tarball is signed.
+ self.setUpSiplKeys()
+ self.openArchive("test", "1.0", "amd64")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ upload = self.process()
+ self.assertEqual(1, upload.signSipl.call_count)
+
def test_signs_combo_image(self):
# Each image in the tarball is signed.
- self.setUpKmodKeys()
self.openArchive("test", "1.0", "amd64")
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
@@ -749,10 +1011,21 @@
self.tarfile.add_file("1.0/empty.opal", b"")
self.tarfile.add_file("1.0/empty2.opal", b"")
self.tarfile.add_file("1.0/empty3.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
+ self.tarfile.add_file("1.0/empty2.sipl", b"")
+ self.tarfile.add_file("1.0/empty3.sipl", b"")
+ self.tarfile.add_file("1.0/empty4.sipl", b"")
+ self.tarfile.add_file("1.0/empty.fit", b"")
+ self.tarfile.add_file("1.0/empty2.fit", b"")
+ self.tarfile.add_file("1.0/empty3.fit", b"")
+ self.tarfile.add_file("1.0/empty4.fit", b"")
+ self.tarfile.add_file("1.0/empty5.fit", b"")
upload = self.process()
self.assertEqual(1, upload.signUefi.call_count)
self.assertEqual(2, upload.signKmod.call_count)
self.assertEqual(3, upload.signOpal.call_count)
+ self.assertEqual(4, upload.signSipl.call_count)
+ self.assertEqual(5, upload.signFit.call_count)
def test_installed(self):
# Files in the tarball are installed correctly.
@@ -824,6 +1097,43 @@
self.assertEqual(stat.S_IMODE(os.stat(self.key).st_mode), 0o600)
self.assertEqual(stat.S_IMODE(os.stat(self.cert).st_mode), 0o644)
+ def test_create_fit_keys_autokey_off(self):
+ # Keys are not created.
+ self.setUpFitKeys(create=False)
+ self.assertFalse(os.path.exists(self.fit_key))
+ self.assertFalse(os.path.exists(self.fit_cert))
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.callLog = FakeMethodCallLog(upload=upload)
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signFit(os.path.join(self.makeTemporaryDirectory(), 'fit'))
+ self.assertEqual(0, upload.callLog.caller_count('Fit keygen'))
+ self.assertFalse(os.path.exists(self.fit_key))
+ self.assertFalse(os.path.exists(self.fit_cert))
+
+ def test_create_fit_keys_autokey_on(self):
+ # Keys are created on demand.
+ self.setUpPPA()
+ self.setUpFitKeys(create=False)
+ self.assertFalse(os.path.exists(self.fit_key))
+ self.assertFalse(os.path.exists(self.fit_cert))
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ fake_copy = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("shutil.copy", fake_copy))
+ upload = SigningUpload()
+ upload.callLog = FakeMethodCallLog(upload=upload)
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signFit(os.path.join(self.makeTemporaryDirectory(), 't.fit'))
+ self.assertEqual(1, upload.callLog.caller_count('Fit keygen'))
+ self.assertTrue(os.path.exists(self.fit_key))
+ self.assertTrue(os.path.exists(self.fit_cert))
+ self.assertEqual(stat.S_IMODE(os.stat(self.fit_key).st_mode), 0o600)
+ self.assertEqual(stat.S_IMODE(os.stat(self.fit_cert).st_mode), 0o644)
+
def test_create_kmod_keys_autokey_off(self):
# Keys are not created.
self.setUpKmodKeys(create=False)
@@ -898,16 +1208,55 @@
self.assertEqual(stat.S_IMODE(os.stat(self.opal_pem).st_mode), 0o600)
self.assertEqual(stat.S_IMODE(os.stat(self.opal_x509).st_mode), 0o644)
+ def test_create_sipl_keys_autokey_off(self):
+ # Keys are not created.
+ self.setUpSiplKeys(create=False)
+ self.assertFalse(os.path.exists(self.sipl_pem))
+ self.assertFalse(os.path.exists(self.sipl_x509))
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.callLog = FakeMethodCallLog(upload=upload)
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signOpal(os.path.join(self.makeTemporaryDirectory(), 't.sipl'))
+ self.assertEqual(0, upload.callLog.caller_count('Sipl keygen key'))
+ self.assertEqual(0, upload.callLog.caller_count('Sipl keygen cert'))
+ self.assertFalse(os.path.exists(self.sipl_pem))
+ self.assertFalse(os.path.exists(self.sipl_x509))
+
+ def test_create_sipl_keys_autokey_on(self):
+ # Keys are created on demand.
+ self.setUpPPA()
+ self.setUpSiplKeys(create=False)
+ self.assertFalse(os.path.exists(self.sipl_pem))
+ self.assertFalse(os.path.exists(self.sipl_x509))
+ fake_call = FakeMethod(result=0)
+ self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+ upload = SigningUpload()
+ upload.callLog = FakeMethodCallLog(upload=upload)
+ upload.setTargetDirectory(
+ self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+ upload.signSipl(os.path.join(self.makeTemporaryDirectory(), 't.sipl'))
+ self.assertEqual(1, upload.callLog.caller_count('Sipl keygen key'))
+ self.assertEqual(1, upload.callLog.caller_count('Sipl keygen cert'))
+ self.assertTrue(os.path.exists(self.sipl_pem))
+ self.assertTrue(os.path.exists(self.sipl_x509))
+ self.assertEqual(stat.S_IMODE(os.stat(self.sipl_pem).st_mode), 0o600)
+ self.assertEqual(stat.S_IMODE(os.stat(self.sipl_x509).st_mode), 0o644)
+
def test_checksumming_tree(self):
# Specifying no options should leave us with an open tree,
# confirm it is checksummed.
self.setUpUefiKeys()
self.setUpKmodKeys()
self.setUpOpalKeys()
+ self.setUpSiplKeys()
self.openArchive("test", "1.0", "amd64")
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
self.process_emulate()
sha256file = os.path.join(self.getSignedPath("test", "amd64"),
"1.0", "SHA256SUMS")
@@ -926,6 +1275,7 @@
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
self.process_emulate()
sha256file = os.path.join(self.getSignedPath("test", "amd64"),
"1.0", "SHA256SUMS")
@@ -949,6 +1299,7 @@
self.tarfile.add_file("1.0/empty.efi", b"")
self.tarfile.add_file("1.0/empty.ko", b"")
self.tarfile.add_file("1.0/empty.opal", b"")
+ self.tarfile.add_file("1.0/empty.sipl", b"")
self.process_emulate()
sha256file = os.path.join(self.getSignedPath("test", "amd64"),
"1.0", "SHA256SUMS")
@@ -982,6 +1333,7 @@
self.tarfile.add_file("1.0/empty.efi", "")
self.tarfile.add_file("1.0/empty.ko", "")
self.tarfile.add_file("1.0/empty.opal", "")
+ self.tarfile.add_file("1.0/empty.sipl", "")
self.process_emulate()
sha256file = os.path.join(self.getSignedPath("test", "amd64"),
"1.0", "SHA256SUMS")
Follow ups