Add a test for two keys marked required = "image" where the FIT carries a
signature from only one of them. Without the binding fix the single
signature satisfies both required keys and the image boots.

The default policy refuses it, required-mode "any" accepts it, and
required-mode "all" refuses it again.

All nine in tree sign-images-*.its hint "dev", and mkimage -g does not
redirect a hint the node already carries, so a prod hinted fixture is
needed to get two keys marked required on the image gate.
sign-images-sha256-rsa2048-prod.its is the in tree
sign-images-sha256-rsa2048.its with its two key-name-hint values changed.

Signed-off-by: anon87111 <[email protected]>
---
 test/py/tests/test_vboot.py                   | 98 ++++++++++++++-----
 .../vboot/sign-images-sha256-rsa2048-prod.its | 42 ++++++++
 2 files changed, 117 insertions(+), 23 deletions(-)
 create mode 100644 test/py/tests/vboot/sign-images-sha256-rsa2048-prod.its

diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py
index d02fa5b16f..db488159ca 100644
--- a/test/py/tests/test_vboot.py
+++ b/test/py/tests/test_vboot.py
@@ -84,27 +84,28 @@ def make_fit(its, ubman, mkimage, dtc_args, datadir, fit):
 # Only run the full suite on a few combinations, since it doesn't add any more
 # test coverage.
 TESTDATA_IN = [
-    ['sha1-basic', 'sha1', '-rsa2048', '', None, False, True, False, False],
-    ['sha1-pad', 'sha1', '-rsa2048', '', '-E -p 0x10000', False, False, False, 
False],
-    ['sha1-pss', 'sha1', '-rsa2048', '-pss', None, False, False, False, False],
-    ['sha1-pss-pad', 'sha1', '-rsa2048', '-pss', '-E -p 0x10000', False, 
False, False, False],
-    ['sha256-basic', 'sha256', '-rsa2048', '', None, False, False, False, 
False],
-    ['sha256-pad', 'sha256', '-rsa2048', '', '-E -p 0x10000', False, False, 
False, False],
-    ['sha256-pss', 'sha256', '-rsa2048', '-pss', None, False, False, False, 
False],
-    ['sha256-pss-pad', 'sha256', '-rsa2048', '-pss', '-E -p 0x10000', False, 
False, False, False],
-    ['sha256-pss-required', 'sha256', '-rsa2048', '-pss', None, True, False, 
False, False],
-    ['sha256-pss-pad-required', 'sha256', '-rsa2048', '-pss', '-E -p 0x10000', 
True, True, False, False],
-    ['sha256-basic-ecdsa256', 'sha256', '-ecdsa256', '', None, False, False, 
False, False],
-    ['sha256-basic-ecdsa384', 'sha256', '-ecdsa384', '', None, False, False, 
False, False],
-    ['sha256-basic-ecdsa521', 'sha256', '-ecdsa521', '', None, False, False, 
False, False],
-    ['sha384-basic', 'sha384', '-rsa3072', '', None, False, False, False, 
False],
-    ['sha384-pad', 'sha384', '-rsa3072', '', '-E -p 0x10000', False, False, 
False, False],
-    ['algo-arg', 'algo-arg', '', '', '-o sha256,rsa2048', False, False, True, 
False],
-    ['sha256-global-sign', 'sha256', '-rsa2048', '', '', False, False, False, 
True],
-    ['sha256-global-sign-pss', 'sha256', '-rsa2048', '-pss', '', False, False, 
False, True],
-    ['sha256-global-sign-ecdsa256', 'sha256', '-ecdsa256', '', '', False, 
False, False, True],
-    ['sha256-global-sign-ecdsa384', 'sha256', '-ecdsa384', '', '', False, 
False, False, True],
-    ['sha256-global-sign-ecdsa521', 'sha256', '-ecdsa521', '', '', False, 
False, False, True],
+    ['sha1-basic', 'sha1', '-rsa2048', '', None, False, True, False, False, 
False],
+    ['sha1-pad', 'sha1', '-rsa2048', '', '-E -p 0x10000', False, False, False, 
False, False],
+    ['sha1-pss', 'sha1', '-rsa2048', '-pss', None, False, False, False, False, 
False],
+    ['sha1-pss-pad', 'sha1', '-rsa2048', '-pss', '-E -p 0x10000', False, 
False, False, False, False],
+    ['sha256-basic', 'sha256', '-rsa2048', '', None, False, False, False, 
False, False],
+    ['sha256-pad', 'sha256', '-rsa2048', '', '-E -p 0x10000', False, False, 
False, False, False],
+    ['sha256-pss', 'sha256', '-rsa2048', '-pss', None, False, False, False, 
False, False],
+    ['sha256-pss-pad', 'sha256', '-rsa2048', '-pss', '-E -p 0x10000', False, 
False, False, False, False],
+    ['sha256-pss-required', 'sha256', '-rsa2048', '-pss', None, True, False, 
False, False, False],
+    ['sha256-pss-pad-required', 'sha256', '-rsa2048', '-pss', '-E -p 0x10000', 
True, True, False, False, False],
+    ['sha256-basic-ecdsa256', 'sha256', '-ecdsa256', '', None, False, False, 
False, False, False],
+    ['sha256-basic-ecdsa384', 'sha256', '-ecdsa384', '', None, False, False, 
False, False, False],
+    ['sha256-basic-ecdsa521', 'sha256', '-ecdsa521', '', None, False, False, 
False, False, False],
+    ['sha384-basic', 'sha384', '-rsa3072', '', None, False, False, False, 
False, False],
+    ['sha384-pad', 'sha384', '-rsa3072', '', '-E -p 0x10000', False, False, 
False, False, False],
+    ['algo-arg', 'algo-arg', '', '', '-o sha256,rsa2048', False, False, True, 
False, False],
+    ['sha256-global-sign', 'sha256', '-rsa2048', '', '', False, False, False, 
True, False],
+    ['sha256-global-sign-pss', 'sha256', '-rsa2048', '-pss', '', False, False, 
False, True, False],
+    ['sha256-global-sign-ecdsa256', 'sha256', '-ecdsa256', '', '', False, 
False, False, True, False],
+    ['sha256-global-sign-ecdsa384', 'sha256', '-ecdsa384', '', '', False, 
False, False, True, False],
+    ['sha256-global-sign-ecdsa521', 'sha256', '-ecdsa521', '', '', False, 
False, False, True, False],
+    ['sha256-required-image', 'sha256', '-rsa2048', '', None, False, False, 
False, False, True],
 ]
 
 # Mark all but the first test as slow, so they are not run with '-k not slow'
@@ -117,10 +118,10 @@ TESTDATA += [pytest.param(*v, marks=pytest.mark.slow) for 
v in TESTDATA_IN[1:]]
 @pytest.mark.requiredtool('fdtget')
 @pytest.mark.requiredtool('fdtput')
 @pytest.mark.requiredtool('openssl')
[email protected]("name,sha_algo,sig_algo,padding,sign_options,required,full_test,algo_arg,global_sign",
[email protected]("name,sha_algo,sig_algo,padding,sign_options,required,full_test,algo_arg,global_sign,required_image",
                          TESTDATA)
 def test_vboot(ubman, name, sha_algo, sig_algo, padding, sign_options, 
required,
-               full_test, algo_arg, global_sign):
+               full_test, algo_arg, global_sign, required_image):
     """Test verified boot signing with mkimage and verification with 'bootm'.
 
     This works using sandbox only as it needs to update the device tree used
@@ -477,6 +478,55 @@ def test_vboot(ubman, name, sha_algo, sig_algo, padding, 
sign_options, required,
                   'Bad Data Hash', False)
         ubman.log.action('%s: Check in-bounds hashed-strings size' % sha_algo)
 
+    def test_required_image_key(sha_algo, sig_algo, padding, sign_options):
+        """Test that a required image key must verify its own signature.
+
+        Two keys are marked required = "image" and the FIT carries a signature
+        from only one of them. Under the default policy the image must be
+        refused. Under required-mode "any" it must boot.
+
+        Args:
+            sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
+            sig_algo: Signature algorithm to use, e.g. '-rsa2048'.
+            padding: Either '' or '-pss', to select the padding to use for the
+                    rsa signature algorithm.
+            sign_options: Options to mkimage when signing a fit image.
+        """
+        # 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', ubman, dtc_args, datadir, tmpdir, dtb)
+        dtc('sandbox-u-boot.dts', ubman, dtc_args, datadir, tmpdir, dtb)
+
+        ubman.log.action('%s: Test FIT with required images' % sha_algo)
+
+        # Sign the images with the prod key and mark it required. This puts the
+        # prod public key into sandbox-u-boot.dtb marked required = "image".
+        make_fit('sign-images-%s%s%s-prod.its' % (sha_algo, sig_algo, padding),
+                 ubman, mkimage, dtc_args, datadir, fit)
+        sign_fit(sha_algo, sign_options)
+
+        # Sign the images with the dev key and mark it required too. This adds
+        # the dev key to the control dtb and overwrites the FIT, which now
+        # carries a dev signature only.
+        make_fit('sign-images-%s%s%s.its' % (sha_algo, sig_algo, padding),
+                 ubman, mkimage, dtc_args, datadir, fit)
+        sign_fit(sha_algo, sign_options)
+
+        # Both keys are now marked required = "image" and the FIT has no prod
+        # signature. The default policy is "all", so this must be refused.
+        run_bootm(sha_algo, 'required image key', '', False)
+
+        # With required-mode "any" the dev signature alone is enough.
+        utils.run_and_log(ubman, 'fdtput -t s %s /signature required-mode any' 
%
+                          dtb)
+        run_bootm(sha_algo, 'required image key any', 'dev+', True)
+
+        # Back to "all" and the missing prod signature must fail again.
+        utils.run_and_log(ubman, 'fdtput -t s %s /signature required-mode all' 
%
+                          dtb)
+        run_bootm(sha_algo, 'required image key all', '', False)
+
     def test_required_key(sha_algo, sig_algo, padding, sign_options):
         """Test verified boot with the given hash algorithm.
 
@@ -619,6 +669,8 @@ def test_vboot(ubman, name, sha_algo, sig_algo, padding, 
sign_options, required,
         ubman.config.dtb = dtb
         if global_sign:
             test_global_sign(sha_algo, sig_algo, padding, sign_options)
+        elif required_image:
+            test_required_image_key(sha_algo, sig_algo, padding, sign_options)
         elif required:
             test_required_key(sha_algo, sig_algo, padding, sign_options)
         else:
diff --git a/test/py/tests/vboot/sign-images-sha256-rsa2048-prod.its 
b/test/py/tests/vboot/sign-images-sha256-rsa2048-prod.its
new file mode 100644
index 0000000000..58e3232de5
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-sha256-rsa2048-prod.its
@@ -0,0 +1,42 @@
+/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>;
+                       signature {
+                               algo = "sha256,rsa2048";
+                               key-name-hint = "prod";
+                       };
+               };
+               fdt-1 {
+                       description = "snow";
+                       data = /incbin/("sandbox-kernel.dtb");
+                       type = "flat_dt";
+                       arch = "sandbox";
+                       compression = "none";
+                       fdt-version = <1>;
+                       signature {
+                               algo = "sha256,rsa2048";
+                               key-name-hint = "prod";
+                       };
+               };
+       };
+       configurations {
+               default = "conf-1";
+               conf-1 {
+                       kernel = "kernel";
+                       fdt = "fdt-1";
+               };
+       };
+};
-- 
2.53.0

Reply via email to