Skip to content

Commit ad6e8e5

Browse files
committed
tests/uefi_sb: Add Windows key upgrade tests
Signed-off-by: Tu Dinh <[email protected]>
1 parent 66f092b commit ad6e8e5

File tree

5 files changed

+136
-3
lines changed

5 files changed

+136
-3
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "contrib/secureboot_objects"]
2+
path = contrib/secureboot_objects
3+
url = https://github.com/microsoft/secureboot_objects.git

contrib/secureboot_objects

Submodule secureboot_objects added at 058c7e6

lib/efi.py

+29-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,34 @@ def getfile(self, suffix=None, prefix=None):
4747
_tempdir = _EfiGlobalTempdir()
4848

4949

50+
class _SecureBootCertList:
51+
_prefix = Path(__file__).parent / '../contrib/secureboot_objects/PreSignedObjects'
52+
53+
def kek_ms_2011(self):
54+
return str(self._prefix / "KEK/Certificates/MicCorKEKCA2011_2011-06-24.der")
55+
56+
def kek_ms_2023(self):
57+
return str(self._prefix / "KEK/Certificates/microsoft corporation kek 2k ca 2023.der")
58+
59+
def db_win_2011(self):
60+
return str(self._prefix / "DB/Certificates/MicWinProPCA2011_2011-10-19.der")
61+
62+
def db_uefi_2011(self):
63+
return str(self._prefix / "DB/Certificates/MicCorUEFCA2011_2011-06-27.der")
64+
65+
def db_win_2023(self):
66+
return str(self._prefix / "DB/Certificates/windows uefi ca 2023.der")
67+
68+
def db_uefi_2023(self):
69+
return str(self._prefix / "DB/Certificates/microsoft uefi ca 2023.der")
70+
71+
def db_oprom_2023(self):
72+
return str(self._prefix / "DB/Certificates/microsoft option rom uefi ca 2023.der")
73+
74+
75+
ms_certs = _SecureBootCertList()
76+
77+
5078
class GUID(UUID):
5179
def as_bytes(self):
5280
return self.bytes_le
@@ -371,8 +399,7 @@ def __init__(
371399
owner_cert: Optional[Certificate] = None,
372400
other_certs: Iterable[Union[Certificate, str]] = None):
373401
assert name in SECURE_BOOT_VARIABLES
374-
# No point having an owner cert without a matching private key
375-
assert owner_cert is None or owner_cert.key is not None
402+
assert owner_cert is None or owner_cert.key is not None, "owner cert must have private key"
376403
self.name = name
377404
self.guid = get_secure_boot_guid(self.name)
378405
self._owner_cert = owner_cert

pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[pytest]
2-
addopts = -ra --maxfail=1
2+
addopts = -ra --maxfail=1 --ignore=contrib/
33
markers =
44
# *** Markers that change test behaviour ***
55
default_vm: mark a test with a default VM in case no --vm parameter was given.

tests/uefi_sb/test_varstored_sb.py

+102
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import logging
22
import pytest
33

4+
from lib.efi import EFIAuth, ms_certs
5+
from lib.vm import VM
6+
47
from .utils import _test_key_exchanges, boot_and_check_no_sb_errors, boot_and_check_sb_failed, \
58
boot_and_check_sb_succeeded, generate_keys, revert_vm_state, sign_efi_bins
69

@@ -153,3 +156,102 @@ def test_key_exchanges(self, uefi_vm):
153156
vm.set_uefi_setup_mode()
154157

155158
_test_key_exchanges(vm)
159+
160+
@pytest.mark.small_vm
161+
@pytest.mark.usefixtures("host_at_least_8_3")
162+
@pytest.mark.usefixtures("windows_vm")
163+
class TestGuestWindowsUEFIKeyUpgrade:
164+
@pytest.fixture(autouse=True)
165+
def setup_and_cleanup(self, uefi_vm_and_snapshot):
166+
vm, snapshot = uefi_vm_and_snapshot
167+
yield
168+
revert_vm_state(vm, snapshot)
169+
170+
def install_old_certs(self, vm: VM):
171+
"""Populate a key set that looks like the old defaults."""
172+
173+
PK = EFIAuth.self_signed("PK")
174+
KEK = EFIAuth.self_signed("KEK", other_certs=[ms_certs.kek_ms_2011()])
175+
db = EFIAuth("db", other_certs=[ms_certs.db_uefi_2011(), ms_certs.db_win_2011()])
176+
# Some test VMs don't like an empty dbx when their own dbx is empty, so just put whatever in there
177+
dbx = EFIAuth.self_signed("dbx")
178+
179+
PK.sign_auth(PK)
180+
PK.sign_auth(KEK)
181+
KEK.sign_auth(db)
182+
KEK.sign_auth(dbx)
183+
184+
vm.install_uefi_certs([PK, KEK, db, dbx])
185+
return [PK, KEK, db, dbx]
186+
187+
def install_new_certs(self, vm: VM, signer: EFIAuth):
188+
"""Populate a key set that looks like the new defaults with 2023 MS keys."""
189+
190+
newPK = EFIAuth.self_signed("PK")
191+
newKEK = EFIAuth("KEK", other_certs=[ms_certs.kek_ms_2011(), ms_certs.kek_ms_2023()])
192+
newdb = EFIAuth(
193+
"db",
194+
other_certs=[
195+
ms_certs.db_win_2011(),
196+
ms_certs.db_win_2023(),
197+
ms_certs.db_uefi_2011(),
198+
ms_certs.db_uefi_2023(),
199+
ms_certs.db_oprom_2023(),
200+
],
201+
)
202+
newdbx = EFIAuth("dbx")
203+
204+
newPK.sign_auth(newPK)
205+
# Technically, there's no need to sign the other databases since we're setting them from Dom0.
206+
# If signing with the old PK works, there'd be no need to test signing with the new PK.
207+
# We use an invalid signer to test scenarios where the user mixes and matches default and custom keys.
208+
signer.sign_auth(newKEK)
209+
signer.sign_auth(newdb)
210+
signer.sign_auth(newdbx)
211+
212+
vm.install_uefi_certs([newPK, newKEK, newdb, newdbx])
213+
214+
def test_key_upgrade(self, uefi_vm: VM):
215+
vm = uefi_vm
216+
vm.param_set("platform", True, key="secureboot")
217+
assert not vm.get_vtpm_uuid()
218+
vm.create_vtpm()
219+
220+
PK, _, _, _ = self.install_old_certs(vm)
221+
boot_and_check_sb_succeeded(vm)
222+
223+
vm.shutdown(verify=True)
224+
225+
self.install_new_certs(vm, PK)
226+
boot_and_check_sb_succeeded(vm)
227+
228+
def test_key_upgrade_bitlocker(self, uefi_vm: VM):
229+
vm = uefi_vm
230+
vm.param_set("platform", True, key="secureboot")
231+
assert not vm.get_vtpm_uuid()
232+
vm.create_vtpm()
233+
234+
PK, _, _, _ = self.install_old_certs(vm)
235+
boot_and_check_sb_succeeded(vm)
236+
237+
vm.execute_powershell_script("Add-WindowsFeature BitLocker,EnhancedStorage")
238+
vm.reboot(verify=True)
239+
240+
vm.execute_powershell_script("Enable-BitLocker $Env:SystemDrive -TpmProtector -UsedSpaceOnly")
241+
# Confirm if PCR7 is bound.
242+
assert vm.execute_powershell_script(
243+
r"""Get-CimInstance -Namespace Root\CIMV2\Security\MicrosoftVolumeEncryption `
244+
-Query "select * from Win32_EncryptableVolume where VolumeType=0" |
245+
Invoke-CimMethod -MethodName GetSecureBootBindingState |
246+
Where-Object ReturnValue -eq 0 |
247+
Select-Object -ExpandProperty BindingState"""
248+
) == "3" # Bound
249+
vm.execute_powershell_script("Suspend-BitLocker $Env:SystemDrive")
250+
vm.shutdown(verify=True)
251+
252+
self.install_new_certs(vm, PK)
253+
boot_and_check_sb_succeeded(vm)
254+
255+
# After Enable-BitLocker, Windows would boot into encryption test.
256+
# If the test failed, Windows would cancel the encryption and give the status FullyDecrypted.
257+
assert vm.execute_powershell_script("(Get-BitLockerVolume $Env:SystemDrive).VolumeStatus") != "FullyDecrypted"

0 commit comments

Comments
 (0)