efi: test/py: authenticate fit capsules
Add support for the authentication of UEFI capsules containing FIT images.
The authentication code is moved out of the function handling raw images
into a new function efi_firmware_capsule_authenticate(). The special case
for the FMP header coming from edk2 tools is preserved. There is no
functional change for capsules containing raw images.
The python test for signed capsules with raw images is renamed with no
functional change and a new test is added for signed capsules containing
FIT images.
This can be tested with sandbox64_defconfig or sandbox_flattree_defconfig,
plus CONFIG_EFI_CAPSULE_AUTHENTICATE=y.
Signed-off-by: Vincent Stehlé <vincent.stehle@arm.com>
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 9cdefab..0ce6c1e 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -177,6 +177,70 @@
return EFI_SUCCESS;
}
+/**
+ * efi_firmware_capsule_authenticate - authenticate the capsule if enabled
+ * @p_image: Pointer to new image
+ * @p_image_size: Pointer to size of new image
+ *
+ * Authenticate the capsule if authentication is enabled.
+ * The image pointer and the image size are updated in case of success.
+ *
+ * Return: status code
+ */
+static
+efi_status_t efi_firmware_capsule_authenticate(const void **p_image,
+ efi_uintn_t *p_image_size)
+{
+ const void *image = *p_image;
+ efi_uintn_t image_size = *p_image_size;
+ u32 fmp_hdr_signature;
+ struct fmp_payload_header *header;
+ void *capsule_payload;
+ efi_status_t status;
+ efi_uintn_t capsule_payload_size;
+
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) {
+ capsule_payload = NULL;
+ capsule_payload_size = 0;
+ status = efi_capsule_authenticate(image, image_size,
+ &capsule_payload,
+ &capsule_payload_size);
+
+ if (status == EFI_SECURITY_VIOLATION) {
+ printf("Capsule authentication check failed. Aborting update\n");
+ return status;
+ } else if (status != EFI_SUCCESS) {
+ return status;
+ }
+
+ debug("Capsule authentication successful\n");
+ image = capsule_payload;
+ image_size = capsule_payload_size;
+ } else {
+ debug("Capsule authentication disabled. ");
+ debug("Updating capsule without authenticating.\n");
+ }
+
+ fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
+ header = (void *)image;
+
+ if (!memcmp(&header->signature, &fmp_hdr_signature,
+ sizeof(fmp_hdr_signature))) {
+ /*
+ * When building the capsule with the scripts in
+ * edk2, a FMP header is inserted above the capsule
+ * payload. Compensate for this header to get the
+ * actual payload that is to be updated.
+ */
+ image += header->header_size;
+ image_size -= header->header_size;
+ }
+
+ *p_image = image;
+ *p_image_size = image_size;
+ return EFI_SUCCESS;
+}
+
#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT
/*
* This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
@@ -265,12 +329,18 @@
efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason)
{
+ efi_status_t status;
+
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason);
if (!image || image_index != 1)
return EFI_EXIT(EFI_INVALID_PARAMETER);
+ status = efi_firmware_capsule_authenticate(&image, &image_size);
+ if (status != EFI_SUCCESS)
+ return EFI_EXIT(status);
+
if (fit_update(image))
return EFI_EXIT(EFI_DEVICE_ERROR);
@@ -371,11 +441,7 @@
efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason)
{
- u32 fmp_hdr_signature;
- struct fmp_payload_header *header;
- void *capsule_payload;
efi_status_t status;
- efi_uintn_t capsule_payload_size;
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason);
@@ -383,44 +449,9 @@
if (!image)
return EFI_EXIT(EFI_INVALID_PARAMETER);
- /* Authenticate the capsule if authentication enabled */
- if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) {
- capsule_payload = NULL;
- capsule_payload_size = 0;
- status = efi_capsule_authenticate(image, image_size,
- &capsule_payload,
- &capsule_payload_size);
-
- if (status == EFI_SECURITY_VIOLATION) {
- printf("Capsule authentication check failed. Aborting update\n");
- return EFI_EXIT(status);
- } else if (status != EFI_SUCCESS) {
- return EFI_EXIT(status);
- }
-
- debug("Capsule authentication successfull\n");
- image = capsule_payload;
- image_size = capsule_payload_size;
- } else {
- debug("Capsule authentication disabled. ");
- debug("Updating capsule without authenticating.\n");
- }
-
- fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
- header = (void *)image;
-
- if (!memcmp(&header->signature, &fmp_hdr_signature,
- sizeof(fmp_hdr_signature))) {
- /*
- * When building the capsule with the scripts in
- * edk2, a FMP header is inserted above the capsule
- * payload. Compensate for this header to get the
- * actual payload that is to be updated.
- */
- image += header->header_size;
- image_size -= header->header_size;
-
- }
+ status = efi_firmware_capsule_authenticate(&image, &image_size);
+ if (status != EFI_SUCCESS)
+ return EFI_EXIT(status);
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
NULL, NULL))
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index 5a8826a..4879f2b 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -97,7 +97,7 @@
shell=True)
if capsule_auth_enabled:
- # firmware signed with proper key
+ # raw firmware signed with proper key
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER.key --certificate SIGNER.crt '
@@ -105,7 +105,7 @@
'u-boot.bin.new Test11'
% (data_dir, u_boot_config.build_dir),
shell=True)
- # firmware signed with *mal* key
+ # raw firmware signed with *mal* key
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER2.key '
@@ -114,6 +114,23 @@
'u-boot.bin.new Test12'
% (data_dir, u_boot_config.build_dir),
shell=True)
+ # FIT firmware signed with proper key
+ check_call('cd %s; '
+ '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+ '--private-key SIGNER.key --certificate SIGNER.crt '
+ '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
+ 'uboot_bin_env.itb Test13'
+ % (data_dir, u_boot_config.build_dir),
+ shell=True)
+ # FIT firmware signed with *mal* key
+ check_call('cd %s; '
+ '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
+ '--private-key SIGNER2.key '
+ '--certificate SIGNER2.crt '
+ '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
+ 'uboot_bin_env.itb Test14'
+ % (data_dir, u_boot_config.build_dir),
+ shell=True)
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
similarity index 89%
copy from test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
copy to test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
index a0b6a1a..4400b8f 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
@@ -1,19 +1,22 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2021, Linaro Limited
-# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+# Copyright (c) 2022, Arm Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>,
+# adapted to FIT images by Vincent Stehlé <vincent.stehle@arm.com>
#
-# U-Boot UEFI: Firmware Update (Signed capsule) Test
+# U-Boot UEFI: Firmware Update (Signed capsule with FIT images) Test
"""
This test verifies capsule-on-disk firmware update
-with signed capsule files
+with signed capsule files containing FIT images
"""
import pytest
from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
-@pytest.mark.boardspec('sandbox')
-@pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+@pytest.mark.boardspec('sandbox64')
+@pytest.mark.boardspec('sandbox_flattree')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_fit')
@pytest.mark.buildconfigspec('efi_capsule_authenticate')
@pytest.mark.buildconfigspec('dfu')
@pytest.mark.buildconfigspec('dfu_sf')
@@ -23,11 +26,11 @@
@pytest.mark.buildconfigspec('cmd_nvedit_efi')
@pytest.mark.buildconfigspec('cmd_sf')
@pytest.mark.slow
-class TestEfiCapsuleFirmwareSigned(object):
+class TestEfiCapsuleFirmwareSignedFit(object):
def test_efi_capsule_auth1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
- Test Case 1 - Update U-Boot on SPI Flash, raw image format
+ Test Case 1 - Update U-Boot on SPI Flash, FIT image format
0x100000-0x150000: U-Boot binary (but dummy)
If the capsule is properly signed, the authentication
@@ -57,11 +60,11 @@
# place a capsule file
output = u_boot_console.run_command_list([
- 'fatload host 0:1 4000000 %s/Test11' % CAPSULE_DATA_DIR,
- 'fatwrite host 0:1 4000000 %s/Test11 $filesize'
+ 'fatload host 0:1 4000000 %s/Test13' % CAPSULE_DATA_DIR,
+ 'fatwrite host 0:1 4000000 %s/Test13 $filesize'
% CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
- assert 'Test11' in ''.join(output)
+ assert 'Test13' in ''.join(output)
# reboot
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
@@ -81,7 +84,7 @@
'0x50000;u-boot-env raw 0x150000 0x200000"',
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
- assert 'Test11' in ''.join(output)
+ assert 'Test13' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
@@ -90,7 +93,7 @@
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
- assert 'Test11' not in ''.join(output)
+ assert 'Test13' not in ''.join(output)
output = u_boot_console.run_command_list([
'sf probe 0:0',
@@ -101,7 +104,7 @@
def test_efi_capsule_auth2(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
- Test Case 2 - Update U-Boot on SPI Flash, raw image format
+ Test Case 2 - Update U-Boot on SPI Flash, FIT image format
0x100000-0x150000: U-Boot binary (but dummy)
If the capsule is signed but with an invalid key,
@@ -132,11 +135,11 @@
# place a capsule file
output = u_boot_console.run_command_list([
- 'fatload host 0:1 4000000 %s/Test12' % CAPSULE_DATA_DIR,
- 'fatwrite host 0:1 4000000 %s/Test12 $filesize'
+ 'fatload host 0:1 4000000 %s/Test14' % CAPSULE_DATA_DIR,
+ 'fatwrite host 0:1 4000000 %s/Test14 $filesize'
% CAPSULE_INSTALL_DIR,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
- assert 'Test12' in ''.join(output)
+ assert 'Test14' in ''.join(output)
# reboot
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
@@ -156,7 +159,7 @@
'0x50000;u-boot-env raw 0x150000 0x200000"',
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
- assert 'Test12' in ''.join(output)
+ assert 'Test14' in ''.join(output)
# need to run uefi command to initiate capsule handling
output = u_boot_console.run_command(
@@ -166,7 +169,7 @@
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
- assert 'Test12' not in ''.join(output)
+ assert 'Test14' not in ''.join(output)
# TODO: check CapsuleStatus in CapsuleXXXX
@@ -179,7 +182,7 @@
def test_efi_capsule_auth3(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
- Test Case 3 - Update U-Boot on SPI Flash, raw image format
+ Test Case 3 - Update U-Boot on SPI Flash, FIT image format
0x100000-0x150000: U-Boot binary (but dummy)
If the capsule is not signed, the authentication
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
similarity index 97%
rename from test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
rename to test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
index a0b6a1a..1b5a1bb 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
@@ -2,11 +2,11 @@
# Copyright (c) 2021, Linaro Limited
# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
#
-# U-Boot UEFI: Firmware Update (Signed capsule) Test
+# U-Boot UEFI: Firmware Update (Signed capsule with raw images) Test
"""
This test verifies capsule-on-disk firmware update
-with signed capsule files
+with signed capsule files containing raw images
"""
import pytest
@@ -23,7 +23,7 @@
@pytest.mark.buildconfigspec('cmd_nvedit_efi')
@pytest.mark.buildconfigspec('cmd_sf')
@pytest.mark.slow
-class TestEfiCapsuleFirmwareSigned(object):
+class TestEfiCapsuleFirmwareSignedRaw(object):
def test_efi_capsule_auth1(
self, u_boot_config, u_boot_console, efi_capsule_data):
"""
@@ -239,7 +239,7 @@
output = u_boot_console.run_command(
'env print -e Capsule0000', wait_for_reboot = True)
- # deleted any way
+ # deleted anyway
output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])