]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
efi: test/py: authenticate fit capsules
authorVincent Stehlé <vincent.stehle@arm.com>
Tue, 31 May 2022 07:55:34 +0000 (09:55 +0200)
committerHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Sat, 4 Jun 2022 06:43:55 +0000 (08:43 +0200)
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>
lib/efi_loader/efi_firmware.c
test/py/tests/test_efi_capsule/conftest.py
test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py [new file with mode: 0644]
test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py [moved from test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py with 98% similarity]

index 9cdefab41fa18cfebb76f11ad8aedb1325294f4f..0ce6c1e34f0259fa1a754dc3f26ad114fdb1b1fa 100644 (file)
@@ -177,6 +177,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
@@ -265,12 +329,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);
 
@@ -371,11 +441,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);
@@ -383,44 +449,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))
index 5a8826a5a6b56d494b9225c7fa5fefbc7e1273ee..4879f2b5c24c06d3910694d4241af5b925f92571 100644 (file)
@@ -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_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
new file mode 100644 (file)
index 0000000..4400b8f
--- /dev/null
@@ -0,0 +1,257 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2021, Linaro Limited
+# 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 with FIT images) Test
+
+"""
+This test verifies capsule-on-disk firmware update
+with signed capsule files containing FIT images
+"""
+
+import pytest
+from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
+
+@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')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+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, FIT image format
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is properly signed, the authentication
+                      should pass and the firmware be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 1-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                '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 'Test13' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 1-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                            '"sf 0:0=u-boot-bin raw 0x100000 '
+                            '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test13' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test13' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:New' in ''.join(output)
+
+    def test_efi_capsule_auth2(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        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,
+                      the authentication should fail and the firmware
+                      not be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 2-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                '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 'Test14' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 2-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test14' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            # deleted any way
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test14' not in ''.join(output)
+
+            # TODO: check CapsuleStatus in CapsuleXXXX
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
+
+    def test_efi_capsule_auth3(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        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
+                      should fail and the firmware not be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 3-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info '
+                        '"sf 0:0=u-boot-bin raw 0x100000 '
+                        '0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old'
+                        % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test02 $filesize'
+                            % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test02' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+                                    + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 3-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info '
+                            '"sf 0:0=u-boot-bin raw 0x100000 '
+                            '0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test02' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000', wait_for_reboot = True)
+
+            # deleted any way
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test02' not in ''.join(output)
+
+            # TODO: check CapsuleStatus in CapsuleXXXX
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
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 a0b6a1ac86f1e4a69450ee67f344e3f6bc1263d3..1b5a1bb42eb4beaacff9b4ba6141d88f0a695816 100644 (file)
@@ -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 @@ 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):
         """
@@ -239,7 +239,7 @@ class TestEfiCapsuleFirmwareSigned(object):
                 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])