[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