From: Thomas Lamprecht Date: Tue, 7 Mar 2023 08:57:30 +0000 (+0100) Subject: debian: sync tests with packaging upstream X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=d7274593bf65b23075b6701e021ab8532b47f3cf;p=pve-edk2-firmware.git debian: sync tests with packaging upstream Signed-off-by: Thomas Lamprecht --- diff --git a/debian/python/UEFI/Filesystems.py b/debian/python/UEFI/Filesystems.py index 0f47cbd..923db30 100644 --- a/debian/python/UEFI/Filesystems.py +++ b/debian/python/UEFI/Filesystems.py @@ -1,5 +1,5 @@ # -# Copyright 2019-2021 Canonical Ltd. +# Copyright 2019-2022 Canonical Ltd. # Authors: # - dann frazier # @@ -87,35 +87,15 @@ class EfiBootableIsoImage: class GrubShellBootableIsoImage(EfiBootableIsoImage): - def __init__(self, efi_arch, use_signed): - EfiArchToGrubArch = { - 'X64': "x86_64", - 'AA64': "arm64", - } + def __init__(self, efi_arch, shim_path, grub_path): efi_img = FatFsImage(64) efi_img.makedirs(os.path.join('EFI', 'BOOT')) removable_media_path = os.path.join( 'EFI', 'BOOT', 'BOOT%s.EFI' % (efi_arch.upper()) ) - efi_ext = 'efi' - grub_subdir = "%s-efi" % EfiArchToGrubArch[efi_arch.upper()] - if use_signed: - efi_ext = "%s.signed" % (efi_ext) - grub_subdir = "%s-signed" % (grub_subdir) - - shim_src = os.path.join( - os.path.sep, 'usr', 'lib', 'shim', - 'shim%s.%s' % (efi_arch.lower(), efi_ext) - ) - grub_src = os.path.join( - os.path.sep, 'usr', 'lib', 'grub', - '%s' % (grub_subdir), - "" if use_signed else "monolithic", - 'grub%s.%s' % (efi_arch.lower(), efi_ext) - ) grub_dest = os.path.join( 'EFI', 'BOOT', 'GRUB%s.EFI' % (efi_arch.upper()) ) - efi_img.insert_file(shim_src, removable_media_path) - efi_img.insert_file(grub_src, grub_dest) + efi_img.insert_file(shim_path, removable_media_path) + efi_img.insert_file(grub_path, grub_dest) super().__init__(efi_img) diff --git a/debian/python/UEFI/Qemu.py b/debian/python/UEFI/Qemu.py index d8aaf23..d4d8668 100644 --- a/debian/python/UEFI/Qemu.py +++ b/debian/python/UEFI/Qemu.py @@ -43,11 +43,10 @@ class QemuEfiFlashSize(enum.Enum): class QemuCommand: - # Based on the args used by ovmf-vars-generator Qemu_Common_Params = [ '-no-user-config', '-nodefaults', '-m', '256', - '-smp', '2,sockets=2,cores=1,threads=1', + '-smp', '1,sockets=1,cores=1,threads=1', '-display', 'none', '-serial', 'stdio', ] diff --git a/debian/python/UEFI/SignedBinary.py b/debian/python/UEFI/SignedBinary.py new file mode 100644 index 0000000..5bb33aa --- /dev/null +++ b/debian/python/UEFI/SignedBinary.py @@ -0,0 +1,52 @@ +# +# Copyright 2022 Canonical Ltd. +# Authors: +# - dann frazier +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, +# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . +# + +import os +import subprocess +import tempfile + + +class SignedBinary: + def __init__(self, binary_path, key_path, cert_path, password=None): + openssl_password_args = [] + if password: + openssl_password_args = [ + "-passin", f"pass:{password}" + ] + with tempfile.NamedTemporaryFile() as keytmp: + subprocess.check_call( + [ + "openssl", "rsa", + ] + openssl_password_args + [ + "-in", f"{key_path}", + "-out", f"{keytmp.name}", + ] + ) + with tempfile.NamedTemporaryFile(delete=False) as f: + self.path = f.name + + subprocess.check_call( + [ + "sbsign", "--key", f"{keytmp.name}", + "--cert", f"{cert_path}", + binary_path, "--output", f"{self.path}" + ] + ) + + def __del__(self): + os.unlink(self.path) diff --git a/debian/tests/control b/debian/tests/control index cc87fde..fbfa279 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -5,6 +5,7 @@ Depends: grub-efi-amd64-signed [amd64], grub-efi-arm64-signed [arm64], mtools [amd64 arm64], + openssl [amd64 arm64], ovmf, ovmf-ia32, python3-pexpect, @@ -12,5 +13,6 @@ Depends: qemu-efi-arm, qemu-system-arm, qemu-system-x86, + sbsigntool [amd64 arm64], shim-signed [amd64 arm64], xorriso [amd64 arm64], diff --git a/debian/tests/shell.py b/debian/tests/shell.py index 391b7bf..de4a699 100755 --- a/debian/tests/shell.py +++ b/debian/tests/shell.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2019-2021 Canonical Ltd. +# Copyright 2019-2022 Canonical Ltd. # Authors: # - dann frazier # @@ -18,12 +18,15 @@ # import enum +import os import pexpect import subprocess import sys +import time import unittest from UEFI.Filesystems import GrubShellBootableIsoImage +from UEFI.SignedBinary import SignedBinary from UEFI.Qemu import QemuEfiMachine, QemuEfiVariant, QemuEfiFlashSize from UEFI import Qemu @@ -31,15 +34,56 @@ DPKG_ARCH = subprocess.check_output( ['dpkg', '--print-architecture'] ).decode().rstrip() +EfiArchToGrubArch = { + 'X64': "x86_64", + 'AA64': "arm64", +} + +TEST_TIMEOUT = 120 + + +def get_local_grub_path(efi_arch, signed=False): + grub_subdir = "%s-efi" % EfiArchToGrubArch[efi_arch.upper()] + ext = "efi" + if signed: + grub_subdir = f"{grub_subdir}-signed" + ext = f"{ext}.signed" + + grub_path = os.path.join( + os.path.sep, 'usr', 'lib', 'grub', + '%s' % (grub_subdir), + "" if signed else "monolithic", + 'grub%s.%s' % (efi_arch.lower(), ext) + ) + return grub_path + + +def get_local_shim_path(efi_arch, signed=False): + ext = 'efi' + if signed: + ext = f"{ext}.signed" + shim_path = os.path.join( + os.path.sep, 'usr', 'lib', 'shim', + 'shim%s.%s' % (efi_arch.lower(), ext) + ) + return shim_path + class BootToShellTest(unittest.TestCase): debug = True + def setUp(self): + self.startTime = time.time() + + def tearDown(self): + t = time.time() - self.startTime + sys.stdout.write("%s runtime: %.3fs\n" % (self.id(), t)) + def run_cmd_check_shell(self, cmd): - child = pexpect.spawn(' '.join(cmd)) + child = pexpect.spawn(' '.join(cmd), encoding='UTF-8') if self.debug: - child.logfile = sys.stdout.buffer + child.logfile = sys.stdout try: while True: i = child.expect( @@ -47,7 +91,7 @@ class BootToShellTest(unittest.TestCase): 'Press .* or any other key to continue', 'Shell> ' ], - timeout=60, + timeout=TEST_TIMEOUT, ) if i == 0: child.sendline('\x1b') @@ -56,7 +100,9 @@ class BootToShellTest(unittest.TestCase): child.sendline('reset -s\r') continue except pexpect.EOF: - return + child.close() + if child.exitstatus != 0: + self.fail("ERROR: exit code %d\n" % (child.exitstatus)) except pexpect.TIMEOUT as err: self.fail("%s\n" % (err)) @@ -65,10 +111,10 @@ class BootToShellTest(unittest.TestCase): PRE_EXEC = 1 POST_EXEC = 2 - child = pexpect.spawn(' '.join(cmd)) + child = pexpect.spawn(' '.join(cmd), encoding='UTF-8') if self.debug: - child.logfile = sys.stdout.buffer + child.logfile = sys.stdout try: state = State.PRE_EXEC while True: @@ -80,7 +126,7 @@ class BootToShellTest(unittest.TestCase): 'grub> ', 'Command Error Status: Access Denied', ], - timeout=60, + timeout=TEST_TIMEOUT, ) if i == 0: child.sendline('\x1b') @@ -102,10 +148,12 @@ class BootToShellTest(unittest.TestCase): if i == 4: verified = False continue + except pexpect.EOF: + child.close() + if child.exitstatus != 0: + self.fail("ERROR: exit code %d\n" % (child.exitstatus)) except pexpect.TIMEOUT as err: self.fail("%s\n" % (err)) - except pexpect.EOF: - pass self.assertEqual(should_verify, verified) def test_aavmf(self): @@ -113,12 +161,20 @@ class BootToShellTest(unittest.TestCase): self.run_cmd_check_shell(q.command) @unittest.skipUnless(DPKG_ARCH == 'arm64', "Requires grub-efi-arm64") + @unittest.skipUnless( + subprocess.run( + ['dpkg-vendor', '--derives-from', 'Ubuntu'] + ).returncode == 0, + "Debian does not provide a signed shim for arm64, see #992073" + ) def test_aavmf_ms_secure_boot_signed(self): q = Qemu.QemuCommand( QemuEfiMachine.AAVMF, variant=QemuEfiVariant.MS, ) - iso = GrubShellBootableIsoImage('AA64', use_signed=True) + grub = get_local_grub_path('AA64', signed=True) + shim = get_local_shim_path('AA64', signed=True) + iso = GrubShellBootableIsoImage('AA64', shim, grub) q.add_disk(iso.path) self.run_cmd_check_secure_boot(q.command, 'aa64', True) @@ -128,7 +184,9 @@ class BootToShellTest(unittest.TestCase): QemuEfiMachine.AAVMF, variant=QemuEfiVariant.MS, ) - iso = GrubShellBootableIsoImage('AA64', use_signed=False) + grub = get_local_grub_path('AA64', signed=False) + shim = get_local_shim_path('AA64', signed=False) + iso = GrubShellBootableIsoImage('AA64', shim, grub) q.add_disk(iso.path) self.run_cmd_check_secure_boot(q.command, 'aa64', False) @@ -178,7 +236,9 @@ class BootToShellTest(unittest.TestCase): variant=QemuEfiVariant.MS, flash_size=QemuEfiFlashSize.SIZE_2MB, ) - iso = GrubShellBootableIsoImage('X64', use_signed=True) + grub = get_local_grub_path('X64', signed=True) + shim = get_local_shim_path('X64', signed=True) + iso = GrubShellBootableIsoImage('X64', shim, grub) q.add_disk(iso.path) self.run_cmd_check_secure_boot(q.command, 'x64', True) @@ -189,7 +249,9 @@ class BootToShellTest(unittest.TestCase): variant=QemuEfiVariant.MS, flash_size=QemuEfiFlashSize.SIZE_2MB, ) - iso = GrubShellBootableIsoImage('X64', use_signed=False) + grub = get_local_grub_path('X64', signed=False) + shim = get_local_shim_path('X64', signed=False) + iso = GrubShellBootableIsoImage('X64', shim, grub) q.add_disk(iso.path) self.run_cmd_check_secure_boot(q.command, 'x64', False) @@ -230,7 +292,9 @@ class BootToShellTest(unittest.TestCase): variant=QemuEfiVariant.MS, flash_size=QemuEfiFlashSize.SIZE_4MB, ) - iso = GrubShellBootableIsoImage('X64', use_signed=True) + grub = get_local_grub_path('X64', signed=True) + shim = get_local_shim_path('X64', signed=True) + iso = GrubShellBootableIsoImage('X64', shim, grub) q.add_disk(iso.path) self.run_cmd_check_secure_boot(q.command, 'x64', True) @@ -241,7 +305,44 @@ class BootToShellTest(unittest.TestCase): variant=QemuEfiVariant.MS, flash_size=QemuEfiFlashSize.SIZE_4MB, ) - iso = GrubShellBootableIsoImage('X64', use_signed=False) + grub = get_local_grub_path('X64', signed=False) + shim = get_local_shim_path('X64', signed=False) + iso = GrubShellBootableIsoImage('X64', shim, grub) + q.add_disk(iso.path) + self.run_cmd_check_secure_boot(q.command, 'x64', False) + + @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only") + def test_ovmf_snakeoil_secure_boot_signed(self): + q = Qemu.QemuCommand( + QemuEfiMachine.OVMF_Q35, + variant=QemuEfiVariant.SNAKEOIL, + ) + shim = SignedBinary( + get_local_shim_path('X64', signed=False), + "/usr/share/ovmf/PkKek-1-snakeoil.key", + "/usr/share/ovmf/PkKek-1-snakeoil.pem", + "snakeoil", + ) + grub = SignedBinary( + get_local_grub_path('X64', signed=False), + "/usr/share/ovmf/PkKek-1-snakeoil.key", + "/usr/share/ovmf/PkKek-1-snakeoil.pem", + "snakeoil", + ) + iso = GrubShellBootableIsoImage('X64', shim.path, grub.path) + q.add_disk(iso.path) + self.run_cmd_check_secure_boot(q.command, 'x64', True) + + @unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only") + def test_ovmf_snakeoil_secure_boot_unsigned(self): + q = Qemu.QemuCommand( + QemuEfiMachine.OVMF_Q35, + variant=QemuEfiVariant.SNAKEOIL, + flash_size=QemuEfiFlashSize.DEFAULT, + ) + grub = get_local_grub_path('X64', signed=False) + shim = get_local_shim_path('X64', signed=False) + iso = GrubShellBootableIsoImage('X64', shim, grub) q.add_disk(iso.path) self.run_cmd_check_secure_boot(q.command, 'x64', False)