All of lore.kernel.org
 help / color / mirror / Atom feed
From: Philippe Reynes <philippe.reynes@softathome.com>
To: sjg@chromium.org, rasmus.villemoes@prevas.dk
Cc: u-boot@lists.denx.de, Philippe Reynes <philippe.reynes@softathome.com>
Subject: [PATCH v8 14/15] test: py: vboot: add test for global image signature
Date: Mon, 28 Mar 2022 22:57:06 +0200	[thread overview]
Message-ID: <20220328205707.348270-15-philippe.reynes@softathome.com> (raw)
In-Reply-To: <20220328205707.348270-1-philippe.reynes@softathome.com>

Adds test units for the pre-load header signature.

Signed-off-by: Philippe Reynes <philippe.reynes@softathome.com>
---
 test/py/tests/test_vboot.py                   | 148 ++++++++++++++++--
 test/py/tests/vboot/sandbox-binman-pss.dts    |  25 +++
 test/py/tests/vboot/sandbox-binman.dts        |  24 +++
 .../tests/vboot/sandbox-u-boot-global-pss.dts |  28 ++++
 test/py/tests/vboot/sandbox-u-boot-global.dts |  27 ++++
 test/py/tests/vboot/simple-images.its         |  36 +++++
 6 files changed, 272 insertions(+), 16 deletions(-)
 create mode 100644 test/py/tests/vboot/sandbox-binman-pss.dts
 create mode 100644 test/py/tests/vboot/sandbox-binman.dts
 create mode 100644 test/py/tests/vboot/sandbox-u-boot-global-pss.dts
 create mode 100644 test/py/tests/vboot/sandbox-u-boot-global.dts
 create mode 100644 test/py/tests/vboot/simple-images.its

diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py
index ac8ed9f114..040147d88b 100644
--- a/test/py/tests/test_vboot.py
+++ b/test/py/tests/test_vboot.py
@@ -21,6 +21,14 @@ For configuration verification:
 - Corrupt the signature
 - Check that image verification no-longer works
 
+For pre-load header verification:
+- Create FIT image with a pre-load header
+- Check that signature verification succeeds
+- Corrupt the FIT image
+- Check that signature verification fails
+- Launch an FIT image without a pre-load header
+- Check that image verification fails
+
 Tests run with both SHA1 and SHA256 hashing.
 """
 
@@ -35,19 +43,21 @@ import vboot_evil
 # Only run the full suite on a few combinations, since it doesn't add any more
 # test coverage.
 TESTDATA = [
-    ['sha1-basic', 'sha1', '', None, False, True, False],
-    ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False],
-    ['sha1-pss', 'sha1', '-pss', None, False, False, False],
-    ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False],
-    ['sha256-basic', 'sha256', '', None, False, False, False],
-    ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False],
-    ['sha256-pss', 'sha256', '-pss', None, False, False, False],
-    ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False],
-    ['sha256-pss-required', 'sha256', '-pss', None, True, False, False],
-    ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False],
-    ['sha384-basic', 'sha384', '', None, False, False, False],
-    ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False],
-    ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True],
+    ['sha1-basic', 'sha1', '', None, False, True, False, False],
+    ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False, False],
+    ['sha1-pss', 'sha1', '-pss', None, False, False, False, False],
+    ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False, False],
+    ['sha256-basic', 'sha256', '', None, False, False, False, False],
+    ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False, False],
+    ['sha256-pss', 'sha256', '-pss', None, False, False, False, False],
+    ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False, False],
+    ['sha256-pss-required', 'sha256', '-pss', None, True, False, False, False],
+    ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False, False],
+    ['sha384-basic', 'sha384', '', None, False, False, False, False],
+    ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False, False],
+    ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True, False],
+    ['sha256-global-sign', 'sha256', '', '', False, False, False, True],
+    ['sha256-global-sign-pss', 'sha256', '-pss', '', False, False, False, True],
 ]
 
 @pytest.mark.boardspec('sandbox')
@@ -56,10 +66,10 @@ TESTDATA = [
 @pytest.mark.requiredtool('fdtget')
 @pytest.mark.requiredtool('fdtput')
 @pytest.mark.requiredtool('openssl')
-@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg",
+@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg,global_sign",
                          TESTDATA)
 def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
-               full_test, algo_arg):
+               full_test, algo_arg, global_sign):
     """Test verified boot signing with mkimage and verification with 'bootm'.
 
     This works using sandbox only as it needs to update the device tree used
@@ -81,6 +91,33 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
         util.run_and_log(cons, 'dtc %s %s%s -O dtb '
                          '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
 
+    def dtc_options(dts, options):
+        """Run the device tree compiler to compile a .dts file
+
+        The output file will be the same as the input file but with a .dtb
+        extension.
+
+        Args:
+            dts: Device tree file to compile.
+            options: Options provided to the compiler.
+        """
+        dtb = dts.replace('.dts', '.dtb')
+        util.run_and_log(cons, 'dtc %s %s%s -O dtb '
+                         '-o %s%s %s' % (dtc_args, datadir, dts, tmpdir, dtb, options))
+
+    def run_binman(dtb):
+        """Run binman to build an image
+
+        Args:
+            dtb: Device tree file used as input file.
+        """
+        pythonpath = os.environ.get('PYTHONPATH', '')
+        os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/../scripts/dtc/pylibfdt' % tmpdir
+        util.run_and_log(cons, [binman, 'build', '-d', "%s/%s" % (tmpdir,dtb),
+                                '-a', "pre-load-key-path=%s" % tmpdir, '-O',
+                                tmpdir, '-I', tmpdir])
+        os.environ['PYTHONPATH'] = pythonpath
+
     def run_bootm(sha_algo, test_type, expect_string, boots, fit=None):
         """Run a 'bootm' command U-Boot.
 
@@ -139,6 +176,23 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
         cons.log.action('%s: Sign images' % sha_algo)
         util.run_and_log(cons, args)
 
+    def sign_fit_dtb(sha_algo, options, dtb):
+        """Sign the FIT
+
+        Signs the FIT and writes the signature into it. It also writes the
+        public key into the dtb.
+
+        Args:
+            sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+                    use.
+            options: Options to provide to mkimage.
+        """
+        args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
+        if options:
+            args += options.split(' ')
+        cons.log.action('%s: Sign images' % sha_algo)
+        util.run_and_log(cons, args)
+
     def sign_fit_norequire(sha_algo, options):
         """Sign the FIT
 
@@ -176,6 +230,20 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
             handle.write(struct.pack(">I", size))
         return struct.unpack(">I", total_size)[0]
 
+    def corrupt_file(fit, offset, value):
+        """Corrupt a file
+
+        To corrupt a file, a value is written at the specified offset
+
+        Args:
+            fit: The file to corrupt
+            offset: Offset to write
+            value: Value written
+        """
+        with open(fit, 'r+b') as handle:
+            handle.seek(offset)
+            handle.write(struct.pack(">I", value))
+
     def create_rsa_pair(name):
         """Generate a new RSA key paid and certificate
 
@@ -374,6 +442,51 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
                          (dtb))
         run_bootm(sha_algo, 'multi required key', '', False)
 
+    def test_global_sign(sha_algo, padding, sign_options):
+        """Test global image signature with the given hash algorithm and padding.
+
+        Args:
+            sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
+            padding: Either '' or '-pss', to select the padding to use for the
+                    rsa signature algorithm.
+        """
+
+        dtb = '%ssandbox-u-boot-global%s.dtb' % (tmpdir, padding)
+        cons.config.dtb = dtb
+
+        # Compile our device tree files for kernel and U-Boot. These are
+        # regenerated here since mkimage will modify them (by adding a
+        # public key) below.
+        dtc('sandbox-kernel.dts')
+        dtc_options('sandbox-u-boot-global%s.dts' % padding, '-p 1024')
+
+        # Build the FIT with dev key (keys NOT required). This adds the
+        # signature into sandbox-u-boot.dtb, NOT marked 'required'.
+        make_fit('simple-images.its')
+        sign_fit_dtb(sha_algo, '', dtb)
+
+        # Build the dtb for binman that define the pre-load header
+        # with the global sigature.
+        dtc('sandbox-binman%s.dts' % padding)
+
+        # Run binman to create the final image with the not signed fit
+        # and the pre-load header that contains the global signature.
+        run_binman('sandbox-binman%s.dtb' % padding)
+
+        # Check that the signature is correctly verified by u-boot
+        run_bootm(sha_algo, 'global image signature',
+                  'signature check has succeed', True, "%ssandbox.img" % tmpdir)
+
+        # Corrupt the image (just one byte after the pre-load header)
+        corrupt_file("%ssandbox.img" % tmpdir, 4096, 255);
+
+        # Check that the signature verification fails
+        run_bootm(sha_algo, 'global image signature',
+                  'signature check has failed', False, "%ssandbox.img" % tmpdir)
+
+        # Check that the boot fails if the global signature is not provided
+        run_bootm(sha_algo, 'global image signature', 'signature is mandatory', False)
+
     cons = u_boot_console
     tmpdir = os.path.join(cons.config.result_dir, name) + '/'
     if not os.path.exists(tmpdir):
@@ -381,6 +494,7 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
     datadir = cons.config.source_dir + '/test/py/tests/vboot/'
     fit = '%stest.fit' % tmpdir
     mkimage = cons.config.build_dir + '/tools/mkimage'
+    binman = cons.config.source_dir + '/tools/binman/binman'
     fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
     dtc_args = '-I dts -O dtb -i %s' % tmpdir
     dtb = '%ssandbox-u-boot.dtb' % tmpdir
@@ -403,7 +517,9 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
         # afterwards.
         old_dtb = cons.config.dtb
         cons.config.dtb = dtb
-        if required:
+        if global_sign:
+            test_global_sign(sha_algo, padding, sign_options)
+        elif required:
             test_required_key(sha_algo, padding, sign_options)
         else:
             test_with_algo(sha_algo, padding, sign_options)
diff --git a/test/py/tests/vboot/sandbox-binman-pss.dts b/test/py/tests/vboot/sandbox-binman-pss.dts
new file mode 100644
index 0000000000..56e3a42fa6
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-binman-pss.dts
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		filename = "sandbox.img";
+
+		pre-load {
+			 content = <&image>;
+			 algo-name = "sha256,rsa2048";
+			 padding-name = "pss";
+			 key-name = "dev.key";
+			 header-size = <4096>;
+			 version = <1>;
+		};
+
+		image: blob-ext {
+			 filename = "test.fit";
+		};
+	};
+};
diff --git a/test/py/tests/vboot/sandbox-binman.dts b/test/py/tests/vboot/sandbox-binman.dts
new file mode 100644
index 0000000000..b24aeba0fa
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-binman.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		filename = "sandbox.img";
+
+		pre-load {
+			 content = <&image>;
+			 algo-name = "sha256,rsa2048";
+			 key-name = "dev.key";
+			 header-size = <4096>;
+			 version = <1>;
+		};
+
+		image: blob-ext {
+			 filename = "test.fit";
+		};
+	};
+};
diff --git a/test/py/tests/vboot/sandbox-u-boot-global-pss.dts b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts
new file mode 100644
index 0000000000..c59a68221b
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	model = "Sandbox Verified Boot Test";
+	compatible = "sandbox";
+
+	binman {
+	};
+
+	reset@0 {
+		compatible = "sandbox,reset";
+	};
+
+	image {
+		pre-load {
+			sig {
+				algo-name = "sha256,rsa2048";
+				padding-name = "pss";
+				signature-size = <256>;
+				mandatory = "yes";
+
+				key-name = "dev";
+			};
+		};
+	};
+};
diff --git a/test/py/tests/vboot/sandbox-u-boot-global.dts b/test/py/tests/vboot/sandbox-u-boot-global.dts
new file mode 100644
index 0000000000..1409f9e1a1
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-u-boot-global.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	model = "Sandbox Verified Boot Test";
+	compatible = "sandbox";
+
+	binman {
+	};
+
+	reset@0 {
+		compatible = "sandbox,reset";
+	};
+
+	image {
+		pre-load {
+			sig {
+				algo-name = "sha256,rsa2048";
+				signature-size = <256>;
+				mandatory = "yes";
+
+				key-name = "dev";
+			};
+		};
+	};
+};
diff --git a/test/py/tests/vboot/simple-images.its b/test/py/tests/vboot/simple-images.its
new file mode 100644
index 0000000000..f62786456b
--- /dev/null
+++ b/test/py/tests/vboot/simple-images.its
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	description = "Chrome OS kernel image with one or more FDT blobs";
+	#address-cells = <1>;
+
+	images {
+		kernel {
+			data = /incbin/("test-kernel.bin");
+			type = "kernel_noload";
+			arch = "sandbox";
+			os = "linux";
+			compression = "none";
+			load = <0x4>;
+			entry = <0x8>;
+			kernel-version = <1>;
+		};
+		fdt-1 {
+			description = "snow";
+			data = /incbin/("sandbox-kernel.dtb");
+			type = "flat_dt";
+			arch = "sandbox";
+			compression = "none";
+			fdt-version = <1>;
+		};
+	};
+	configurations {
+		default = "conf-1";
+		conf-1 {
+			kernel = "kernel";
+			fdt = "fdt-1";
+		};
+	};
+};
-- 
2.25.1


  parent reply	other threads:[~2022-03-28 21:00 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-28 20:56 [PATCH v8 00/15] image: add a stage pre-load Philippe Reynes
2022-03-28 20:56 ` [PATCH v8 01/15] arch: Kconfig: imply BINMAN for SANDBOX Philippe Reynes
2022-03-31 23:20   ` Tom Rini
2022-03-28 20:56 ` [PATCH v8 02/15] lib: Kconfig: enhance help for ASN1 Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:56 ` [PATCH v8 03/15] lib: Kconfig: enhance the help of OID_REGISTRY Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:56 ` [PATCH v8 04/15] lib: allow to build asn1 decoder and oid registry in SPL Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:56 ` [PATCH v8 05/15] lib: crypto: allow to build crypyo " Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:56 ` [PATCH v8 06/15] lib: rsa: allow rsa verify with pkey " Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:56 ` [PATCH v8 07/15] boot: image: add a stage pre-load Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:57 ` [PATCH v8 08/15] cmd: bootm: " Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:57 ` [PATCH v8 09/15] common: spl: fit_ram: allow to use image pre load Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:57 ` [PATCH v8 10/15] mkimage: add public key for image pre-load stage Philippe Reynes
2022-03-31 23:21   ` Tom Rini
2022-03-28 20:57 ` [PATCH v8 11/15] Makefile: provide sah-key to binman Philippe Reynes
2022-03-31 23:22   ` Tom Rini
2022-03-28 20:57 ` [PATCH v8 12/15] tools: binman: add support for pre-load header Philippe Reynes
2022-03-31 23:22   ` Tom Rini
2022-03-28 20:57 ` [PATCH v8 13/15] configs: sandbox_defconfig: enable stage pre-load in bootm Philippe Reynes
2022-03-31 23:22   ` Tom Rini
2022-03-28 20:57 ` Philippe Reynes [this message]
2022-03-31 23:22   ` [PATCH v8 14/15] test: py: vboot: add test for global image signature Tom Rini
2022-03-28 20:57 ` [PATCH v8 15/15] cmd: bootm: add subcommand preload Philippe Reynes
2022-03-31 23:22   ` Tom Rini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220328205707.348270-15-philippe.reynes@softathome.com \
    --to=philippe.reynes@softathome.com \
    --cc=rasmus.villemoes@prevas.dk \
    --cc=sjg@chromium.org \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.