From patchwork Wed Feb 26 04:54:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 1244666 X-Patchwork-Delegate: xypron.glpk@gmx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=lPOpz2a8; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48S3SD5VH8z9sPR for ; Wed, 26 Feb 2020 15:57:00 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 2D8A08121C; Wed, 26 Feb 2020 05:55:58 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="lPOpz2a8"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 25209808BA; Wed, 26 Feb 2020 05:55:34 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-pj1-x1029.google.com (mail-pj1-x1029.google.com [IPv6:2607:f8b0:4864:20::1029]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 0EAF1810E0 for ; Wed, 26 Feb 2020 05:55:25 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=takahiro.akashi@linaro.org Received: by mail-pj1-x1029.google.com with SMTP id d5so733814pjz.5 for ; Tue, 25 Feb 2020 20:55:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RT80ml6FzlBhaUFPnyjj4B0pdvYyDxf3gWx5nGb+lb4=; b=lPOpz2a8QV0c8RlGgbAj/GjLZ6sYvzkB/Y06/XnA7n74ls2oFwQ0IqJ6KbXr9RO1zy Vzh53iY85xRTR/Pq5kW/tv6PziaMzRxBd32GPPXNOjR6i4RerAiCBrMGVbZKLa2Mnicr B/Q9WHsRS6SiF0zwjLmP6rRl0zo6vjMiOTxlFaadZWxbSJnbleIbbmbwK0TlvWWr2C7S vZGNxgklmg6Q+3efBDFgXkhRSoTD+hdDIZcs54o5EOHYEwP6kXl6CUcKX9PguJbsLyuA nz8RkR9uCZPC/8chAhKnrB3NrZaMlaIcPdnvMAhcT3AgjYT6LMyjIvPh3MHmxkZe79mk 3crw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RT80ml6FzlBhaUFPnyjj4B0pdvYyDxf3gWx5nGb+lb4=; b=OMemCftcThWe9Z+plBEh4CosvaOwzMqwIznZA9HKTRzAXR554O6R0qt97BuvReyqJx Nr0ypAsH/1FSNlCowRams69V5gfTvkSfpcbTUkweOrE0/n1XRnO7SdFVVHNKKZWD7s49 8V7lftFcIl6lmtkipfiIXveM30LdZhTBom/pKGx39mQx74jWc8twhLZqMvZ1dUfzpI6L aEHQU6YeVDKM8UdkTdOr41hwQi7yU2tVUa9XYeMz2eE37r6nyYCaktIKQSTRFZi46Lvf Ho1HWmdwVS7gzZBmbMmhP4T/WCz5b4toWjDo5Xc47tVYHpFikTXpL1bwe5PzV0DJx07Y I0eg== X-Gm-Message-State: APjAAAV//0SV7JCZmI8deBG07D8POeUI2LuS1x6NIfaSM/WlDAOynhhG qdqgFAIaLHW3YYJ3tMRbB/T5sw== X-Google-Smtp-Source: APXvYqwg3YdTjuPOvsVbXnJt9qMV4A5IRp2Hu+9YhF3AaH6ksycRrM3bneD6FeZP+Y00dJsk6sgrzA== X-Received: by 2002:a17:90a:f17:: with SMTP id 23mr2850951pjy.84.1582692923188; Tue, 25 Feb 2020 20:55:23 -0800 (PST) Received: from linaro.org ([121.95.100.191]) by smtp.googlemail.com with ESMTPSA id l25sm676023pgn.47.2020.02.25.20.55.22 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 25 Feb 2020 20:55:22 -0800 (PST) From: AKASHI Takahiro To: xypron.glpk@gmx.de, agraf@csgraf.de, trini@konsulko.com Cc: sjg@chromium.org, ilias.apalodimas@linaro.org, mail@patrick-wildt.de, u-boot@lists.denx.de, AKASHI Takahiro Subject: [PATCH v6 12/16] efi_loader, pytest: set up secure boot environment Date: Wed, 26 Feb 2020 13:54:44 +0900 Message-Id: <20200226045448.2453-13-takahiro.akashi@linaro.org> X-Mailer: git-send-email 2.24.0 In-Reply-To: <20200226045448.2453-1-takahiro.akashi@linaro.org> References: <20200226045448.2453-1-takahiro.akashi@linaro.org> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean 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 --- test/py/README.md | 8 ++ test/py/tests/test_efi_secboot/conftest.py | 151 +++++++++++++++++++++ test/py/tests/test_efi_secboot/defs.py | 21 +++ 3 files changed, 180 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/test/py/README.md b/test/py/README.md index 3cbe01b73e28..aa8a5607b064 100644 --- a/test/py/README.md +++ b/test/py/README.md @@ -37,7 +37,15 @@ will be required. The following is an incomplete list: | openssl | | sudo OR guestmount | | e2fsprogs | +| util-linux | +| coreutils | | dosfstools | +| efitools | +| mount | +| mtools | +| sbsigntool | +| 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..e542fef6e819 --- /dev/null +++ b/test/py/tests/test_efi_secboot/conftest.py @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro + +import os +import os.path +import pytest +import re +from subprocess import call, check_call, check_output, CalledProcessError +from defs import * + +# from test/py/conftest.py +def tool_is_in_path(tool): + for path in os.environ["PATH"].split(os.pathsep): + fn = os.path.join(path, tool) + if os.path.isfile(fn) and os.access(fn, os.X_OK): + return True + return False + +# +# Fixture for UEFI secure boot test +# +@pytest.fixture(scope='session') +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: + non_root = tool_is_in_path('udisksctl') + + # 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) + if non_root: + 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) + else: + loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' + % (part_size, image_path), shell=True).decode() + mnt_point = '/mnt' + check_output('sudo mount -t %s -o umask=000 %s %s' + % (fs_type, loop_dev, mnt_point), shell=True) + + # 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) + + if non_root: + check_call('udisksctl unmount -b %s' % loop_dev, shell=True) + # not needed + # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True) + else: + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + + except CalledProcessError as e: + pytest.skip('Setup failed: %s' % e.cmd) + return + else: + yield image_path + finally: + 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=''