diff mbox series

[U-Boot,14/16] efi_loader, pytest: set up secure boot environment

Message ID 20191113005306.30356-15-takahiro.akashi@linaro.org
State Changes Requested, archived
Delegated to: Heinrich Schuchardt
Headers show
Series efi_loader: add secure boot support | expand

Commit Message

AKASHI Takahiro Nov. 13, 2019, 12:53 a.m. UTC
A fixture for UEFI secure boot tests (image authentication and variable
authentication) is defined. A small file system with test data in a single
partition formatted in fat is created.

This test requires efitools v1.5.2 or later. If the system's efitools
is older, you have to build it on your own and define EFITOOLS_PATH.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 .travis.yml                                |   4 +
 test/py/README.md                          |   4 +
 test/py/tests/test_efi_secboot/conftest.py | 128 +++++++++++++++++++++
 test/py/tests/test_efi_secboot/defs.py     |  21 ++++
 4 files changed, 157 insertions(+)
 create mode 100644 test/py/tests/test_efi_secboot/conftest.py
 create mode 100644 test/py/tests/test_efi_secboot/defs.py

Comments

Heinrich Schuchardt Nov. 16, 2019, 8:19 p.m. UTC | #1
On 11/13/19 1:53 AM, AKASHI Takahiro wrote:
> A fixture for UEFI secure boot tests (image authentication and variable
> authentication) is defined. A small file system with test data in a single
> partition formatted in fat is created.

Why do we need a file system? That seems overly complicated.

Can't we use tFTP to download the file you want to check like we do for
helloworld.efi and grub.efi.

>
> This test requires efitools v1.5.2 or later. If the system's efitools
> is older, you have to build it on your own and define EFITOOLS_PATH.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   .travis.yml                                |   4 +
>   test/py/README.md                          |   4 +
>   test/py/tests/test_efi_secboot/conftest.py | 128 +++++++++++++++++++++
>   test/py/tests/test_efi_secboot/defs.py     |  21 ++++
>   4 files changed, 157 insertions(+)
>   create mode 100644 test/py/tests/test_efi_secboot/conftest.py
>   create mode 100644 test/py/tests/test_efi_secboot/defs.py
>
> diff --git a/.travis.yml b/.travis.yml
> index 1e9837869508..edb87fc6396d 100644
> --- a/.travis.yml
> +++ b/.travis.yml
> @@ -38,6 +38,10 @@ addons:
>       - libisl15
>       - clang-7
>       - srecord
> +    - coreutils
> +    - dosfstools
> +    - efitools
> +    - udisks2
>
>   install:
>    # Clone uboot-test-hooks
> diff --git a/test/py/README.md b/test/py/README.md
> index 3cbe01b73e28..bb8d6c9d21dd 100644
> --- a/test/py/README.md
> +++ b/test/py/README.md
> @@ -38,6 +38,10 @@ will be required.  The following is an incomplete list:
>   | sudo OR guestmount |
>   | e2fsprogs      |
>   | dosfstools     |
> +| coreutils      |
> +| efitools       |
> +| sbsigntools    |
> +| udisks2        |
>
>   Please use the apporirate commands for your distribution to match these tools
>   up with the package that provides them.
> diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py
> new file mode 100644
> index 000000000000..e50f749ad1a6
> --- /dev/null
> +++ b/test/py/tests/test_efi_secboot/conftest.py
> @@ -0,0 +1,128 @@
> +# SPDX-License-Identifier:      GPL-2.0+
> +# Copyright (c) 2019, Linaro Limited
> +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> +
> +import os
> +import os.path
> +import pytest
> +import re
> +from subprocess import call, check_call, check_output, CalledProcessError
> +from defs import *
> +
> +#
> +# Fixture for UEFI secure boot test
> +#
> +# NOTE: yield_fixture was deprecated since pytest-3.0
> +@pytest.yield_fixture()

If it is deprecated, don't use it.

https://docs.pytest.org/en/latest/yieldfixture.html

Best regards

Heinrich

> +def efi_boot_env(request, u_boot_config):
> +    """Set up a file system to be used in UEFI secure boot test.
> +
> +    Args:
> +        request: Pytest request object.
> +	u_boot_config: U-boot configuration.
> +
> +    Return:
> +        A path to disk image to be used for testing
> +    """
> +    global HELLO_PATH
> +
> +    image_path = u_boot_config.persistent_data_dir
> +    image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME
> +    image_size = EFI_SECBOOT_IMAGE_SIZE
> +    part_size = EFI_SECBOOT_PART_SIZE
> +    fs_type = EFI_SECBOOT_FS_TYPE
> +
> +    if HELLO_PATH == '':
> +        HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi'
> +
> +    try:
> +        # create a disk/partition
> +        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
> +                            % (image_path, image_size), shell=True)
> +        check_call('sgdisk %s -n 1:0:+%dMiB'
> +                            % (image_path, part_size), shell=True)
> +        # create a file system
> +        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
> +                            % (image_path, part_size), shell=True)
> +        check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True)
> +        check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
> +                            % (image_path, image_path, 1), shell=True)
> +        check_call('rm %s.tmp' % image_path, shell=True)
> +        out_data = check_output('udisksctl loop-setup -f %s -o %d'
> +                            % (image_path, 1048576), shell=True).decode()
> +        m = re.search('(?<= as )(.*)\.', out_data)
> +        loop_dev = m.group(1)
> +        # print 'loop device is: %s' % loop_dev
> +        out_data = check_output('udisksctl info -b %s' % loop_dev, shell=True).decode()
> +        m = re.search('MountPoints:[ \t]+(.*)', out_data)
> +        mnt_point = m.group(1)
> +        # print 'mount point is: %s' % mnt_point
> +
> +        # suffix
> +        # *.key: RSA private key in PEM
> +        # *.crt: X509 certificate (self-signed) in PEM
> +        # *.esl: signature list
> +        # *.hash: message digest of image as signature list
> +        # *.auth: signed signature list in signature database format
> +        # *.efi: UEFI image
> +        # *.efi.signed: signed UEFI image
> +
> +        # Create signature database
> +        ## PK
> +        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
> +                            % mnt_point, shell=True)
> +        check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
> +                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> +                            shell=True)
> +        ## PK_null for deletion
> +        check_call('cd %s; sleep 2; touch PK_null.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
> +                            % (mnt_point, EFITOOLS_PATH), shell=True)
> +        ## KEK
> +        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
> +                            % mnt_point, shell=True)
> +        check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
> +                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> +                            shell=True)
> +        ## db
> +        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
> +                            % mnt_point, shell=True)
> +        check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth'
> +                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> +                            shell=True)
> +        ## db1
> +        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
> +                            % mnt_point, shell=True)
> +        check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db1.esl db1.auth'
> +                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> +                            shell=True)
> +        ## db1-update
> +        check_call('cd %s; %ssign-efi-sig-list -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth'
> +                            % (mnt_point, EFITOOLS_PATH), shell=True)
> +        ## dbx
> +        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
> +                            % mnt_point, shell=True)
> +        check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
> +                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> +                            shell=True)
> +
> +        # Copy image
> +        check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True)
> +
> +        ## Sign image
> +        check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
> +                            % mnt_point, shell=True)
> +        ## Digest image
> +        check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
> +                            % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
> +                            shell=True)
> +
> +        check_call('udisksctl unmount -b %s' % loop_dev, shell=True)
> +        # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True)
> +    except CalledProcessError as e:
> +        pytest.skip('Setup failed: %s' % e.cmd)
> +        return
> +    else:
> +        yield image_path
> +    finally:
> +        call('echo %s' % image_path, shell=True)
> +        # call('rm -f %s' % image_path, shell=True)
> diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py
> new file mode 100644
> index 000000000000..d6222809c547
> --- /dev/null
> +++ b/test/py/tests/test_efi_secboot/defs.py
> @@ -0,0 +1,21 @@
> +# SPDX-License-Identifier:      GPL-2.0+
> +
> +# Disk image name
> +EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img'
> +
> +# Size in MiB
> +EFI_SECBOOT_IMAGE_SIZE=16
> +EFI_SECBOOT_PART_SIZE=8
> +
> +# Partition file system type
> +EFI_SECBOOT_FS_TYPE='vfat'
> +
> +# Owner guid
> +GUID='11111111-2222-3333-4444-123456789abc'
> +
> +# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
> +# you need build a newer version on your own.
> +EFITOOLS_PATH=''
> +
> +# Hello World application for sandbox
> +HELLO_PATH=''
>
AKASHI Takahiro Nov. 18, 2019, 5:52 a.m. UTC | #2
Heinrich,

On Sat, Nov 16, 2019 at 09:19:33PM +0100, Heinrich Schuchardt wrote:
> On 11/13/19 1:53 AM, AKASHI Takahiro wrote:
> >A fixture for UEFI secure boot tests (image authentication and variable
> >authentication) is defined. A small file system with test data in a single
> >partition formatted in fat is created.
> 
> Why do we need a file system? That seems overly complicated.

I disagree. As UEFI requires a support for file system, fat, assuming
that we can access a file system is fair enough.

> Can't we use tFTP to download the file you want to check like we do for
> helloworld.efi and grub.efi.

Let me ask the same question as you did.
Why do we need a network device?

> >
> >This test requires efitools v1.5.2 or later. If the system's efitools
> >is older, you have to build it on your own and define EFITOOLS_PATH.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  .travis.yml                                |   4 +
> >  test/py/README.md                          |   4 +
> >  test/py/tests/test_efi_secboot/conftest.py | 128 +++++++++++++++++++++
> >  test/py/tests/test_efi_secboot/defs.py     |  21 ++++
> >  4 files changed, 157 insertions(+)
> >  create mode 100644 test/py/tests/test_efi_secboot/conftest.py
> >  create mode 100644 test/py/tests/test_efi_secboot/defs.py
> >
> >diff --git a/.travis.yml b/.travis.yml
> >index 1e9837869508..edb87fc6396d 100644
> >--- a/.travis.yml
> >+++ b/.travis.yml
> >@@ -38,6 +38,10 @@ addons:
> >      - libisl15
> >      - clang-7
> >      - srecord
> >+    - coreutils
> >+    - dosfstools
> >+    - efitools
> >+    - udisks2
> >
> >  install:
> >   # Clone uboot-test-hooks
> >diff --git a/test/py/README.md b/test/py/README.md
> >index 3cbe01b73e28..bb8d6c9d21dd 100644
> >--- a/test/py/README.md
> >+++ b/test/py/README.md
> >@@ -38,6 +38,10 @@ will be required.  The following is an incomplete list:
> >  | sudo OR guestmount |
> >  | e2fsprogs      |
> >  | dosfstools     |
> >+| coreutils      |
> >+| efitools       |
> >+| sbsigntools    |
> >+| udisks2        |
> >
> >  Please use the apporirate commands for your distribution to match these tools
> >  up with the package that provides them.
> >diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py
> >new file mode 100644
> >index 000000000000..e50f749ad1a6
> >--- /dev/null
> >+++ b/test/py/tests/test_efi_secboot/conftest.py
> >@@ -0,0 +1,128 @@
> >+# SPDX-License-Identifier:      GPL-2.0+
> >+# Copyright (c) 2019, Linaro Limited
> >+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >+
> >+import os
> >+import os.path
> >+import pytest
> >+import re
> >+from subprocess import call, check_call, check_output, CalledProcessError
> >+from defs import *
> >+
> >+#
> >+# Fixture for UEFI secure boot test
> >+#
> >+# NOTE: yield_fixture was deprecated since pytest-3.0
> >+@pytest.yield_fixture()
> 
> If it is deprecated, don't use it.

Okay, I have forgot to fix it.

-Takahiro Akashi

> https://docs.pytest.org/en/latest/yieldfixture.html
> 
> Best regards
> 
> Heinrich
> 
> >+def efi_boot_env(request, u_boot_config):
> >+    """Set up a file system to be used in UEFI secure boot test.
> >+
> >+    Args:
> >+        request: Pytest request object.
> >+	u_boot_config: U-boot configuration.
> >+
> >+    Return:
> >+        A path to disk image to be used for testing
> >+    """
> >+    global HELLO_PATH
> >+
> >+    image_path = u_boot_config.persistent_data_dir
> >+    image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME
> >+    image_size = EFI_SECBOOT_IMAGE_SIZE
> >+    part_size = EFI_SECBOOT_PART_SIZE
> >+    fs_type = EFI_SECBOOT_FS_TYPE
> >+
> >+    if HELLO_PATH == '':
> >+        HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi'
> >+
> >+    try:
> >+        # create a disk/partition
> >+        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
> >+                            % (image_path, image_size), shell=True)
> >+        check_call('sgdisk %s -n 1:0:+%dMiB'
> >+                            % (image_path, part_size), shell=True)
> >+        # create a file system
> >+        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
> >+                            % (image_path, part_size), shell=True)
> >+        check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True)
> >+        check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
> >+                            % (image_path, image_path, 1), shell=True)
> >+        check_call('rm %s.tmp' % image_path, shell=True)
> >+        out_data = check_output('udisksctl loop-setup -f %s -o %d'
> >+                            % (image_path, 1048576), shell=True).decode()
> >+        m = re.search('(?<= as )(.*)\.', out_data)
> >+        loop_dev = m.group(1)
> >+        # print 'loop device is: %s' % loop_dev
> >+        out_data = check_output('udisksctl info -b %s' % loop_dev, shell=True).decode()
> >+        m = re.search('MountPoints:[ \t]+(.*)', out_data)
> >+        mnt_point = m.group(1)
> >+        # print 'mount point is: %s' % mnt_point
> >+
> >+        # suffix
> >+        # *.key: RSA private key in PEM
> >+        # *.crt: X509 certificate (self-signed) in PEM
> >+        # *.esl: signature list
> >+        # *.hash: message digest of image as signature list
> >+        # *.auth: signed signature list in signature database format
> >+        # *.efi: UEFI image
> >+        # *.efi.signed: signed UEFI image
> >+
> >+        # Create signature database
> >+        ## PK
> >+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
> >+                            % mnt_point, shell=True)
> >+        check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
> >+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> >+                            shell=True)
> >+        ## PK_null for deletion
> >+        check_call('cd %s; sleep 2; touch PK_null.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
> >+                            % (mnt_point, EFITOOLS_PATH), shell=True)
> >+        ## KEK
> >+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
> >+                            % mnt_point, shell=True)
> >+        check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
> >+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> >+                            shell=True)
> >+        ## db
> >+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
> >+                            % mnt_point, shell=True)
> >+        check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth'
> >+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> >+                            shell=True)
> >+        ## db1
> >+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
> >+                            % mnt_point, shell=True)
> >+        check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db1.esl db1.auth'
> >+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> >+                            shell=True)
> >+        ## db1-update
> >+        check_call('cd %s; %ssign-efi-sig-list -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth'
> >+                            % (mnt_point, EFITOOLS_PATH), shell=True)
> >+        ## dbx
> >+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
> >+                            % mnt_point, shell=True)
> >+        check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
> >+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
> >+                            shell=True)
> >+
> >+        # Copy image
> >+        check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True)
> >+
> >+        ## Sign image
> >+        check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
> >+                            % mnt_point, shell=True)
> >+        ## Digest image
> >+        check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
> >+                            % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
> >+                            shell=True)
> >+
> >+        check_call('udisksctl unmount -b %s' % loop_dev, shell=True)
> >+        # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True)
> >+    except CalledProcessError as e:
> >+        pytest.skip('Setup failed: %s' % e.cmd)
> >+        return
> >+    else:
> >+        yield image_path
> >+    finally:
> >+        call('echo %s' % image_path, shell=True)
> >+        # call('rm -f %s' % image_path, shell=True)
> >diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py
> >new file mode 100644
> >index 000000000000..d6222809c547
> >--- /dev/null
> >+++ b/test/py/tests/test_efi_secboot/defs.py
> >@@ -0,0 +1,21 @@
> >+# SPDX-License-Identifier:      GPL-2.0+
> >+
> >+# Disk image name
> >+EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img'
> >+
> >+# Size in MiB
> >+EFI_SECBOOT_IMAGE_SIZE=16
> >+EFI_SECBOOT_PART_SIZE=8
> >+
> >+# Partition file system type
> >+EFI_SECBOOT_FS_TYPE='vfat'
> >+
> >+# Owner guid
> >+GUID='11111111-2222-3333-4444-123456789abc'
> >+
> >+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
> >+# you need build a newer version on your own.
> >+EFITOOLS_PATH=''
> >+
> >+# Hello World application for sandbox
> >+HELLO_PATH=''
> >
>
diff mbox series

Patch

diff --git a/.travis.yml b/.travis.yml
index 1e9837869508..edb87fc6396d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,6 +38,10 @@  addons:
     - libisl15
     - clang-7
     - srecord
+    - coreutils
+    - dosfstools
+    - efitools
+    - udisks2
 
 install:
  # Clone uboot-test-hooks
diff --git a/test/py/README.md b/test/py/README.md
index 3cbe01b73e28..bb8d6c9d21dd 100644
--- a/test/py/README.md
+++ b/test/py/README.md
@@ -38,6 +38,10 @@  will be required.  The following is an incomplete list:
 | sudo OR guestmount |
 | e2fsprogs      |
 | dosfstools     |
+| coreutils      |
+| efitools       |
+| sbsigntools    |
+| udisks2        |
 
 Please use the apporirate commands for your distribution to match these tools
 up with the package that provides them.
diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py
new file mode 100644
index 000000000000..e50f749ad1a6
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/conftest.py
@@ -0,0 +1,128 @@ 
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import pytest
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+from defs import *
+
+#
+# Fixture for UEFI secure boot test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def efi_boot_env(request, u_boot_config):
+    """Set up a file system to be used in UEFI secure boot test.
+
+    Args:
+        request: Pytest request object.
+	u_boot_config: U-boot configuration.
+
+    Return:
+        A path to disk image to be used for testing
+    """
+    global HELLO_PATH
+
+    image_path = u_boot_config.persistent_data_dir
+    image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME
+    image_size = EFI_SECBOOT_IMAGE_SIZE
+    part_size = EFI_SECBOOT_PART_SIZE
+    fs_type = EFI_SECBOOT_FS_TYPE
+
+    if HELLO_PATH == '':
+        HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi'
+
+    try:
+        # create a disk/partition
+        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
+                            % (image_path, image_size), shell=True)
+        check_call('sgdisk %s -n 1:0:+%dMiB'
+                            % (image_path, part_size), shell=True)
+        # create a file system
+        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
+                            % (image_path, part_size), shell=True)
+        check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True)
+        check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
+                            % (image_path, image_path, 1), shell=True)
+        check_call('rm %s.tmp' % image_path, shell=True)
+        out_data = check_output('udisksctl loop-setup -f %s -o %d'
+                            % (image_path, 1048576), shell=True).decode()
+        m = re.search('(?<= as )(.*)\.', out_data)
+        loop_dev = m.group(1)
+        # print 'loop device is: %s' % loop_dev
+        out_data = check_output('udisksctl info -b %s' % loop_dev, shell=True).decode()
+        m = re.search('MountPoints:[ \t]+(.*)', out_data)
+        mnt_point = m.group(1)
+        # print 'mount point is: %s' % mnt_point
+
+        # suffix
+        # *.key: RSA private key in PEM
+        # *.crt: X509 certificate (self-signed) in PEM
+        # *.esl: signature list
+        # *.hash: message digest of image as signature list
+        # *.auth: signed signature list in signature database format
+        # *.efi: UEFI image
+        # *.efi.signed: signed UEFI image
+
+        # Create signature database
+        ## PK
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## PK_null for deletion
+        check_call('cd %s; sleep 2; touch PK_null.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
+                            % (mnt_point, EFITOOLS_PATH), shell=True)
+        ## KEK
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## db
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## db1
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db1.esl db1.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## db1-update
+        check_call('cd %s; %ssign-efi-sig-list -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth'
+                            % (mnt_point, EFITOOLS_PATH), shell=True)
+        ## dbx
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+
+        # Copy image
+        check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True)
+
+        ## Sign image
+        check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
+                            % mnt_point, shell=True)
+        ## Digest image
+        check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
+                            % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+                            shell=True)
+
+        check_call('udisksctl unmount -b %s' % loop_dev, shell=True)
+        # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True)
+    except CalledProcessError as e:
+        pytest.skip('Setup failed: %s' % e.cmd)
+        return
+    else:
+        yield image_path
+    finally:
+        call('echo %s' % image_path, shell=True)
+        # call('rm -f %s' % image_path, shell=True)
diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py
new file mode 100644
index 000000000000..d6222809c547
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/defs.py
@@ -0,0 +1,21 @@ 
+# SPDX-License-Identifier:      GPL-2.0+
+
+# Disk image name
+EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img'
+
+# Size in MiB
+EFI_SECBOOT_IMAGE_SIZE=16
+EFI_SECBOOT_PART_SIZE=8
+
+# Partition file system type
+EFI_SECBOOT_FS_TYPE='vfat'
+
+# Owner guid
+GUID='11111111-2222-3333-4444-123456789abc'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+EFITOOLS_PATH=''
+
+# Hello World application for sandbox
+HELLO_PATH=''