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
