← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~apw/launchpad/signing-opal into lp:launchpad

 

Andy Whitcroft has proposed merging lp:~apw/launchpad/signing-opal into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~apw/launchpad/signing-opal/+merge/328108

Add signing support for vmlinux for use on ppc64el Opal (and compatible) firmware.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~apw/launchpad/signing-opal into lp:launchpad.
=== modified file 'lib/lp/archivepublisher/signing.py'
--- lib/lp/archivepublisher/signing.py	2016-08-10 09:19:16 +0000
+++ lib/lp/archivepublisher/signing.py	2017-07-26 16:08:51 +0000
@@ -94,12 +94,16 @@
             self.uefi_cert = None
             self.kmod_pem = None
             self.kmod_x509 = None
+            self.opal_pem = None
+            self.opal_x509 = None
             self.autokey = False
         else:
             self.uefi_key = os.path.join(pubconf.signingroot, "uefi.key")
             self.uefi_cert = os.path.join(pubconf.signingroot, "uefi.crt")
             self.kmod_pem = os.path.join(pubconf.signingroot, "kmod.pem")
             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.autokey = pubconf.signingautokey
 
         self.setComponents(tarfile_path)
@@ -171,6 +175,8 @@
                     yield (os.path.join(dirpath, filename), self.signUefi)
                 elif filename.endswith(".ko"):
                     yield (os.path.join(dirpath, filename), self.signKmod)
+                elif filename.endswith(".opal"):
+                    yield (os.path.join(dirpath, filename), self.signOpal)
 
     def getKeys(self, which, generate, *keynames):
         """Validate and return the uefi key and cert for encryption."""
@@ -237,15 +243,15 @@
         cmdl = ["sbsign", "--key", key, "--cert", cert, image]
         return self.callLog("UEFI signing", cmdl)
 
-    def generateKmodKeys(self):
-        """Generate new Kernel Signing Keys for this archive."""
-        directory = os.path.dirname(self.kmod_pem)
+    def generatePemX509Pair(self, key_type, 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, "kmod")
+            self.archive.owner.name, self.archive.name, key_type)
 
         old_mask = os.umask(0o077)
         try:
@@ -276,21 +282,26 @@
                 new_key_cmd = [
                     'openssl', 'req', '-new', '-nodes', '-utf8', '-sha512',
                     '-days', '3650', '-batch', '-x509', '-config', tf.name,
-                    '-outform', 'PEM', '-out', self.kmod_pem,
-                    '-keyout', self.kmod_pem
+                    '-outform', 'PEM', '-out', pem_filename,
+                    '-keyout', pem_filename
                     ]
-                if self.callLog("Kmod keygen key", new_key_cmd) == 0:
+                if self.callLog(key_type + " keygen key", new_key_cmd) == 0:
                     new_x509_cmd = [
-                        'openssl', 'x509', '-in', self.kmod_pem,
-                        '-outform', 'DER', '-out', self.kmod_x509
+                        'openssl', 'x509', '-in', pem_filename,
+                        '-outform', 'DER', '-out', x509_filename
                         ]
-                    if self.callLog("Kmod keygen cert", new_x509_cmd) != 0:
-                        os.unlink(self.kmod_pem)
+                    if self.callLog(key_type + " keygen cert",
+                                    new_x509_cmd) != 0:
+                        os.unlink(pem_filename)
         finally:
             os.umask(old_mask)
 
-        if os.path.exists(self.kmod_x509):
-            os.chmod(self.kmod_x509, 0o644)
+        if os.path.exists(x509_filename):
+            os.chmod(x509_filename, 0o644)
+
+    def generateKmodKeys(self):
+        """Generate new Kernel Signing Keys for this archive."""
+        self.generatePemX509Pair("Kmod", self.kmod_pem, self.kmod_x509)
 
     def signKmod(self, image):
         """Attempt to sign a kernel module."""
@@ -303,6 +314,21 @@
         cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
         return self.callLog("Kmod signing", cmdl)
 
+    def generateOpalKeys(self):
+        """Generate new Opal Signing Keys for this archive."""
+        self.generatePemX509Pair("Opal", self.opal_pem, self.opal_x509)
+
+    def signOpal(self, image):
+        """Attempt to sign a kernel image for Opal."""
+        remove_if_exists("%s.sig" % image)
+        (pem, cert) = self.getKeys('Opal Kernel', self.generateOpalKeys,
+            self.opal_pem, self.opal_x509)
+        if not pem or not cert:
+            return
+        self.publishPublicKey(cert)
+        cmdl = ["kmodsign", "-D", "sha512", pem, cert, image, image + ".sig"]
+        return self.callLog("Opal 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	2017-07-24 10:06:38 +0000
+++ lib/lp/archivepublisher/tests/test_signing.py	2017-07-26 16:08:51 +0000
@@ -11,6 +11,7 @@
 
 from fixtures import MonkeyPatch
 from testtools.deferredruntest import AsynchronousDeferredRunTest
+from testtools.matchers import FileExists, Not, MatchesListwise
 from twisted.internet import defer
 from zope.component import getUtility
 
@@ -44,10 +45,13 @@
         self.upload = upload
         self.callers = {
             "UEFI signing": 0,
+            "UEFI keygen": 0,
             "Kmod signing": 0,
-            "UEFI keygen": 0,
             "Kmod keygen key": 0,
             "Kmod keygen cert": 0,
+            "Opal signing": 0,
+            "Opal keygen key": 0,
+            "Opal keygen cert": 0,
             }
 
     def __call__(self, *args, **kwargs):
@@ -61,6 +65,10 @@
             if filename.endswith(".efi"):
                 write_file(filename + ".signed", "")
 
+        elif description == "UEFI keygen":
+            write_file(self.upload.uefi_key, "")
+            write_file(self.upload.uefi_cert, "")
+
         elif description == "Kmod signing":
             filename = cmdl[-1]
             if filename.endswith(".ko.sig"):
@@ -72,9 +80,16 @@
         elif description == "Kmod keygen key":
             write_file(self.upload.kmod_pem, "")
 
-        elif description == "UEFI keygen":
-            write_file(self.upload.uefi_key, "")
-            write_file(self.upload.uefi_cert, "")
+        elif description == "Opal signing":
+            filename = cmdl[-1]
+            if filename.endswith(".opal.sig"):
+                write_file(filename, "")
+
+        elif description == "Opal keygen cert":
+            write_file(self.upload.opal_x509, "")
+
+        elif description == "Opal keygen key":
+            write_file(self.upload.opal_pem, "")
 
         else:
             raise AssertionError("unknown command executed cmd=(%s)" %
@@ -85,6 +100,9 @@
     def caller_count(self, caller):
         return self.callers.get(caller, 0)
 
+    def caller_list(self):
+        return [ (c,n) for (c,n) in self.callers.items() if n != 0 ]
+
 
 class TestSigningHelpers(TestCaseWithFactory):
 
@@ -147,6 +165,13 @@
             write_file(self.kmod_pem, "")
             write_file(self.kmod_x509, "")
 
+    def setUpOpalKeys(self, create=True):
+        self.opal_pem = os.path.join(self.signing_dir, "opal.pem")
+        self.opal_x509 = os.path.join(self.signing_dir, "opal.x509")
+        if create:
+            write_file(self.opal_pem, "")
+            write_file(self.opal_x509, "")
+
     def openArchive(self, loader_type, version, arch):
         self.path = os.path.join(
             self.temp_dir, "%s_%s_%s.tar.gz" % (loader_type, version, arch))
@@ -157,6 +182,10 @@
         pubconf = getPubConfig(self.archive)
         return os.path.join(pubconf.archiveroot, "dists", self.suite, "main")
 
+    def assertFilesIn(self, file_matches, base):
+        files = [ os.path.join(base, f) for f in file_matches[0::2] ]
+        self.assertThat(files, MatchesListwise(file_matches[1::2]))
+
 
 class TestSigning(TestSigningHelpers):
 
@@ -182,6 +211,7 @@
         upload = SigningUpload()
         upload.signUefi = FakeMethod()
         upload.signKmod = FakeMethod()
+        upload.signOpal = FakeMethod()
         # Under no circumstances is it safe to execute actual commands.
         fake_call = FakeMethod(result=0)
         self.useFixture(MonkeyPatch("subprocess.call", fake_call))
@@ -201,12 +231,9 @@
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/empty.efi", "")
         self.tarfile.add_file("1.0/empty.ko", "")
+        self.tarfile.add_file("1.0/empty.opal", "")
         upload = self.process_emulate()
-        self.assertEqual(0, upload.callLog.caller_count('UEFI keygen'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod keygen key'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod keygen cert'))
-        self.assertEqual(0, upload.callLog.caller_count('UEFI signing'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod signing'))
+        self.assertContentEqual([], upload.callLog.caller_list())
 
     def test_archive_primary_no_keys(self):
         # If the configured key/cert are missing, processing succeeds but
@@ -214,12 +241,9 @@
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/empty.efi", "")
         self.tarfile.add_file("1.0/empty.ko", "")
+        self.tarfile.add_file("1.0/empty.opal", "")
         upload = self.process_emulate()
-        self.assertEqual(0, upload.callLog.caller_count('UEFI keygen'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod keygen key'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod keygen cert'))
-        self.assertEqual(0, upload.callLog.caller_count('UEFI signing'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod signing'))
+        self.assertContentEqual([], upload.callLog.caller_list())
 
     def test_archive_primary_keys(self):
         # If the configured key/cert are missing, processing succeeds but
@@ -229,12 +253,13 @@
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/empty.efi", "")
         self.tarfile.add_file("1.0/empty.ko", "")
+        self.tarfile.add_file("1.0/empty.opal", "")
         upload = self.process_emulate()
-        self.assertEqual(0, upload.callLog.caller_count('UEFI keygen'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod keygen key'))
-        self.assertEqual(0, upload.callLog.caller_count('Kmod keygen cert'))
-        self.assertEqual(1, upload.callLog.caller_count('UEFI signing'))
-        self.assertEqual(1, upload.callLog.caller_count('Kmod signing'))
+        expected_callers = [
+            ('UEFI signing', 1),
+            ('Kmod signing', 1),
+        ]
+        self.assertContentEqual(expected_callers, upload.callLog.caller_list())
 
     def test_PPA_creates_keys(self):
         # If the configured key/cert are missing, processing succeeds but
@@ -243,12 +268,19 @@
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/empty.efi", "")
         self.tarfile.add_file("1.0/empty.ko", "")
+        self.tarfile.add_file("1.0/empty.opal", "")
         upload = self.process_emulate()
-        self.assertEqual(1, upload.callLog.caller_count('UEFI keygen'))
-        self.assertEqual(1, upload.callLog.caller_count('Kmod keygen key'))
-        self.assertEqual(1, upload.callLog.caller_count('Kmod keygen cert'))
-        self.assertEqual(1, upload.callLog.caller_count('UEFI signing'))
-        self.assertEqual(1, upload.callLog.caller_count('Kmod signing'))
+        expected_callers = [
+            ('UEFI keygen', 1),
+            ('Kmod keygen key', 1),
+            ('Kmod keygen cert', 1),
+            ('Opal keygen key', 1),
+            ('Opal keygen cert', 1),
+            ('UEFI signing', 1),
+            ('Kmod signing', 1),
+            ('Opal signing', 1),
+        ]
+        self.assertContentEqual(expected_callers, upload.callLog.caller_list())
 
     def test_common_name_plain(self):
         upload = SigningUpload()
@@ -319,42 +351,51 @@
         # Specifying no options should leave us with an open tree.
         self.setUpUefiKeys()
         self.setUpKmodKeys()
+        self.setUpOpalKeys()
         self.openArchive("test", "1.0", "amd64")
         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.process_emulate()
-        self.assertTrue(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.efi")))
-        self.assertTrue(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.efi.signed")))
-        self.assertTrue(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.ko")))
-        self.assertTrue(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.ko.sig")))
+        files = [
+            "1.0/empty.efi", FileExists(),
+            "1.0/empty.efi.signed", FileExists(),
+            "1.0/empty.ko", FileExists(),
+            "1.0/empty.ko.sig", FileExists(),
+            "1.0/empty.opal", FileExists(),
+            "1.0/empty.opal.sig", FileExists(),
+            ]
+        self.assertFilesIn(files, self.getSignedPath("test", "amd64"))
 
     def test_options_tarball(self):
         # Specifying the "tarball" option should create an tarball in
         # the tmpdir.
         self.setUpUefiKeys()
         self.setUpKmodKeys()
+        self.setUpOpalKeys()
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/control/options", "tarball")
         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.process_emulate()
-        self.assertFalse(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.efi")))
-        self.assertFalse(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.ko")))
+        files = [
+            "1.0/empty.efi", Not(FileExists()),
+            "1.0/empty.ko", Not(FileExists()),
+            "1.0/empty.opal", Not(FileExists()),
+            "1.0/signed.tar.gz", FileExists(),
+        ]
+        self.assertFilesIn(files, self.getSignedPath("test", "amd64"))
         tarfilename = os.path.join(self.getSignedPath("test", "amd64"),
             "1.0", "signed.tar.gz")
-        self.assertTrue(os.path.exists(tarfilename))
         with tarfile.open(tarfilename) as tarball:
             self.assertContentEqual([
-                '1.0', '1.0/control', '1.0/control/kmod.x509',
-                '1.0/control/uefi.crt', '1.0/empty.efi',
-                '1.0/empty.efi.signed', '1.0/empty.ko', '1.0/empty.ko.sig',
-                '1.0/control/options',
+                '1.0', '1.0/control', '1.0/control/options',
+                '1.0/control/kmod.x509', '1.0/control/uefi.crt',
+                '1.0/control/opal.x509',
+                '1.0/empty.efi', '1.0/empty.efi.signed',
+                '1.0/empty.ko', '1.0/empty.ko.sig',
+                '1.0/empty.opal', '1.0/empty.opal.sig',
                 ], tarball.getnames())
 
     def test_options_signed_only(self):
@@ -362,19 +403,22 @@
         # the source files leaving signatures only.
         self.setUpUefiKeys()
         self.setUpKmodKeys()
+        self.setUpOpalKeys()
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/control/options", "signed-only")
         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.process_emulate()
-        self.assertFalse(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.efi")))
-        self.assertTrue(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.efi.signed")))
-        self.assertFalse(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.ko")))
-        self.assertTrue(os.path.exists(os.path.join(
-            self.getSignedPath("test", "amd64"), "1.0", "empty.ko.sig")))
+        files = [
+            "1.0/empty.efi", Not(FileExists()),
+            "1.0/empty.ko", Not(FileExists()),
+            "1.0/empty.opal", Not(FileExists()),
+            "1.0/empty.efi.signed", FileExists(),
+            "1.0/empty.ko.sig", FileExists(),
+            "1.0/empty.opal.sig", FileExists(),
+        ]
+        self.assertFilesIn(files, self.getSignedPath("test", "amd64"))
 
     def test_options_tarball_signed_only(self):
         # Specifying the "tarball" option should create an tarball in
@@ -382,20 +426,24 @@
         # original files.
         self.setUpUefiKeys()
         self.setUpKmodKeys()
+        self.setUpOpalKeys()
         self.openArchive("test", "1.0", "amd64")
         self.tarfile.add_file("1.0/control/options",
             "tarball\nsigned-only")
         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.process_emulate()
         tarfilename = os.path.join(self.getSignedPath("test", "amd64"),
             "1.0", "signed.tar.gz")
         self.assertTrue(os.path.exists(tarfilename))
         with tarfile.open(tarfilename) as tarball:
             self.assertContentEqual([
-                '1.0', '1.0/control', '1.0/control/kmod.x509',
-                '1.0/control/uefi.crt', '1.0/empty.efi.signed',
-                '1.0/empty.ko.sig', '1.0/control/options',
+                '1.0', '1.0/control', '1.0/control/options',
+                '1.0/control/uefi.crt', '1.0/control/kmod.x509',
+                '1.0/control/opal.x509',
+                '1.0/empty.efi.signed', '1.0/empty.ko.sig',
+                '1.0/empty.opal.sig',
                 ], tarball.getnames())
 
     def test_no_signed_files(self):
@@ -512,7 +560,7 @@
         upload.generateKmodKeys = FakeMethod()
         upload.setTargetDirectory(
             self.archive, "test_1.0_amd64.tar.gz", "distroseries")
-        upload.signUefi('t.ko')
+        upload.signKmod('t.ko')
         self.assertEqual(0, fake_call.call_count)
         self.assertEqual(0, upload.generateKmodKeys.call_count)
 
@@ -547,6 +595,72 @@
             ]
         self.assertEqual(expected_cmd, args)
 
+    def test_correct_opal_signing_command_executed(self):
+        # Check that calling signKmod() will generate the expected command
+        # when appropriate keys are present.
+        self.setUpOpalKeys()
+        fake_call = FakeMethod(result=0)
+        self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+        upload = SigningUpload()
+        upload.generateOpalKeys = FakeMethod()
+        upload.setTargetDirectory(
+            self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+        upload.signOpal('t.opal')
+        self.assertEqual(1, fake_call.call_count)
+        # Assert command form.
+        args = fake_call.calls[0][0][0]
+        expected_cmd = [
+            'kmodsign', '-D', 'sha512', self.opal_pem, self.opal_x509,
+            't.opal', 't.opal.sig'
+            ]
+        self.assertEqual(expected_cmd, args)
+        self.assertEqual(0, upload.generateOpalKeys.call_count)
+
+    def test_correct_opal_signing_command_executed_no_keys(self):
+        # Check that calling signKmod() will generate no commands when
+        # no keys are present.
+        self.setUpOpalKeys(create=False)
+        fake_call = FakeMethod(result=0)
+        self.useFixture(MonkeyPatch("subprocess.call", fake_call))
+        upload = SigningUpload()
+        upload.generateOpalKeys = FakeMethod()
+        upload.setTargetDirectory(
+            self.archive, "test_1.0_amd64.tar.gz", "distroseries")
+        upload.signOpal('t.opal')
+        self.assertEqual(0, fake_call.call_count)
+        self.assertEqual(0, upload.generateOpalKeys.call_count)
+
+    def test_correct_opal_keygen_command_executed(self):
+        # Check that calling generateUefiKeys() will generate the
+        # expected command.
+        self.setUpPPA()
+        self.setUpOpalKeys(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.generateOpalKeys()
+        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.opal_pem, '-keyout', self.opal_pem
+            ]
+        self.assertEqual(expected_cmd, args)
+        args = fake_call.calls[1][0][0]
+        expected_cmd = [
+            'openssl', 'x509', '-in', self.opal_pem, '-outform', 'DER',
+            '-out', self.opal_x509
+            ]
+        self.assertEqual(expected_cmd, args)
+
     def test_signs_uefi_image(self):
         # Each image in the tarball is signed.
         self.setUpUefiKeys()
@@ -563,6 +677,14 @@
         upload = self.process()
         self.assertEqual(1, upload.signKmod.call_count)
 
+    def test_signs_opal_image(self):
+        # Each image in the tarball is signed.
+        self.setUpOpalKeys()
+        self.openArchive("test", "1.0", "amd64")
+        self.tarfile.add_file("1.0/empty.opal", "")
+        upload = self.process()
+        self.assertEqual(1, upload.signOpal.call_count)
+
     def test_signs_combo_image(self):
         # Each image in the tarball is signed.
         self.setUpKmodKeys()
@@ -570,9 +692,13 @@
         self.tarfile.add_file("1.0/empty.efi", "")
         self.tarfile.add_file("1.0/empty.ko", "")
         self.tarfile.add_file("1.0/empty2.ko", "")
+        self.tarfile.add_file("1.0/empty.opal", "")
+        self.tarfile.add_file("1.0/empty2.opal", "")
+        self.tarfile.add_file("1.0/empty3.opal", "")
         upload = self.process()
         self.assertEqual(1, upload.signUefi.call_count)
         self.assertEqual(2, upload.signKmod.call_count)
+        self.assertEqual(3, upload.signOpal.call_count)
 
     def test_installed(self):
         # Files in the tarball are installed correctly.
@@ -681,14 +807,53 @@
         self.assertEqual(stat.S_IMODE(os.stat(self.kmod_pem).st_mode), 0o600)
         self.assertEqual(stat.S_IMODE(os.stat(self.kmod_x509).st_mode), 0o644)
 
+    def test_create_opal_keys_autokey_off(self):
+        # Keys are not created.
+        self.setUpOpalKeys(create=False)
+        self.assertFalse(os.path.exists(self.opal_pem))
+        self.assertFalse(os.path.exists(self.opal_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.opal'))
+        self.assertEqual(0, upload.callLog.caller_count('Opal keygen key'))
+        self.assertEqual(0, upload.callLog.caller_count('Opal keygen cert'))
+        self.assertFalse(os.path.exists(self.opal_pem))
+        self.assertFalse(os.path.exists(self.opal_x509))
+
+    def test_create_opal_keys_autokey_on(self):
+        # Keys are created on demand.
+        self.setUpPPA()
+        self.setUpOpalKeys(create=False)
+        self.assertFalse(os.path.exists(self.opal_pem))
+        self.assertFalse(os.path.exists(self.opal_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.opal'))
+        self.assertEqual(1, upload.callLog.caller_count('Opal keygen key'))
+        self.assertEqual(1, upload.callLog.caller_count('Opal keygen cert'))
+        self.assertTrue(os.path.exists(self.opal_pem))
+        self.assertTrue(os.path.exists(self.opal_x509))
+        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_checksumming_tree(self):
         # Specifying no options should leave us with an open tree,
         # confirm it is checksummed.
         self.setUpUefiKeys()
         self.setUpKmodKeys()
+        self.setUpOpalKeys()
         self.openArchive("test", "1.0", "amd64")
         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.process_emulate()
         sha256file = os.path.join(self.getSignedPath("test", "amd64"),
              "1.0", "SHA256SUMS")
@@ -702,9 +867,11 @@
         yield self.setUpArchiveKey()
         self.setUpUefiKeys()
         self.setUpKmodKeys()
+        self.setUpOpalKeys()
         self.openArchive("test", "1.0", "amd64")
         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.process_emulate()
         sha256file = os.path.join(self.getSignedPath("test", "amd64"),
              "1.0", "SHA256SUMS")


Follow ups