[PATCH 2/2] efi: test/py: authenticate fit capsules

Vincent Stehlé vincent.stehle at arm.com
Tue May 31 09:55:34 CEST 2022


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 at arm.com>
Cc: Heinrich Schuchardt <xypron.glpk at gmx.de>
---
 lib/efi_loader/efi_firmware.c                 | 115 +++++++++++-------
 test/py/tests/test_efi_capsule/conftest.py    |  21 +++-
 ...py => test_capsule_firmware_signed_fit.py} |  41 ++++---
 ...py => test_capsule_firmware_signed_raw.py} |   6 +-
 4 files changed, 117 insertions(+), 66 deletions(-)
 copy test/py/tests/test_efi_capsule/{test_capsule_firmware_signed.py => test_capsule_firmware_signed_fit.py} (89%)
 rename test/py/tests/test_efi_capsule/{test_capsule_firmware_signed.py => test_capsule_firmware_signed_raw.py} (98%)

diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index fe4e084106d..cbe29e90789 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -178,6 +178,70 @@ static efi_status_t efi_fill_image_desc_array(
 	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
@@ -266,12 +330,18 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
 	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);
 
@@ -372,11 +442,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
 	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);
@@ -384,44 +450,9 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
 	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 5a8826a5a6b..4879f2b5c24 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -97,7 +97,7 @@ def efi_capsule_data(request, u_boot_config):
                    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 @@ def efi_capsule_data(request, u_boot_config):
                             '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 @@ def efi_capsule_data(request, u_boot_config):
                             '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 a0b6a1ac86f..4400b8f1368 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 at linaro.org>
+# Copyright (c) 2022, Arm Limited
+# Author: AKASHI Takahiro <takahiro.akashi at linaro.org>,
+#         adapted to FIT images by Vincent Stehlé <vincent.stehle at 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
 
- at pytest.mark.boardspec('sandbox')
- at pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+ at pytest.mark.boardspec('sandbox64')
+ at pytest.mark.boardspec('sandbox_flattree')
+ at 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 @@ from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
 @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 @@ class TestEfiCapsuleFirmwareSigned(object):
 
             # 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 @@ class TestEfiCapsuleFirmwareSigned(object):
                             '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 @@ class TestEfiCapsuleFirmwareSigned(object):
             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 @@ class TestEfiCapsuleFirmwareSigned(object):
     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 @@ class TestEfiCapsuleFirmwareSigned(object):
 
             # 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 @@ class TestEfiCapsuleFirmwareSigned(object):
                         '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 @@ class TestEfiCapsuleFirmwareSigned(object):
             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 @@ class TestEfiCapsuleFirmwareSigned(object):
     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 98%
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 a0b6a1ac86f..8201a544e0c 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 at 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 @@ from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
 @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):
         """
-- 
2.35.1



More information about the U-Boot mailing list