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

Heinrich Schuchardt xypron.glpk at gmx.de
Sat Jun 4 09:01:00 CEST 2022


On 6/1/22 01:53, AKASHI Takahiro wrote:
> On Tue, May 31, 2022 at 09:55:34AM +0200, Vincent Stehl?? wrote:
>> Add support for the authentication of UEFI capsules containing FIT images.
>
> Thank you for adding this enhancement.
> Sughosh could and should have done it from the beginning.
>
>> 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.

Please, provide a patch to update the defconfigs accordingly.

>
> I hope that the 'capsule authentication' tests, either FIT or raw, be
> run in CI loop even if we need end up adding new sandbox config files.

test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py ss
test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py ...
test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py sss

Only the firemware_raw test is executed. All others are skipped currently.

Best regards

Heinrich

>
> -Takahiro Akashi
>
>
>> 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