| Message ID | 20250313184926.2605982-1-adrian.freihofer@siemens.com |
|---|---|
| State | Accepted, archived |
| Commit | dda1fcbc85c26d1851dda6ed235238b15939998e |
| Headers | show |
| Series | [v2] oe-selftest: fitimage add more u-boot tests | expand |
On 3/13/25 7:49 PM, Adrian Freihofer wrote: > Add a new test function which checks that the device-tree of U-Boot > contains the public keys which are required for checking the signature > of the kernel FIT image at run-time. > > Use this new _check_kernel_dtb function in the existing > test_sign_cascaded_uboot_fit_image test case which already creates a > build configuration with UBOOT_SIGN_ENABLE = "1" and keys for the kernel. > But so far there was no check that the keys for the kernel verification > got added to U-Boot's DTB. > This test case checks the configuration where only the configuration > nodes of the kernel FIT image are signed. > > A new test case test_sign_uboot_kernel_individual checks the > configuration with two keys and signed image and signed configuration > nodes. > This test case covers the use case which recently broke with commit: > OE-Core rev: 259bfa86f384206f0d0a96a5b84887186c5f689e > u-boot: kernel-fitimage: Fix dependency loop if > UBOOT_SIGN_ENABLE and UBOOT_ENV enabled > and got fixed with commit > OE-Core rev: 0106e5efab99c8016836a2ab71e2327ce58a9a9d > u-boot: kernel-fitimage: Restore FIT_SIGN_INDIVIDUAL="1" > behavior > > This patch also fixes a few more details: > - Use the get function to access the bb_var dicts where appropriate. > - log bb_vars if oe-selftest is called with -v > - Simplify the code by moving all the U-Boot related variables to the > _fit_get_bb_vars function. > - Do not set FIT_GENERATE_KEYS = "1" without inheriting the > kernel-fitimage.bbclass which handles this variable. > > Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com> > --- > meta/lib/oeqa/selftest/cases/fitimage.py | 130 ++++++++++++++++++++--- > 1 file changed, 118 insertions(+), 12 deletions(-) > > diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py > index 721628d8e73..d79c8cf321a 100644 > --- a/meta/lib/oeqa/selftest/cases/fitimage.py > +++ b/meta/lib/oeqa/selftest/cases/fitimage.py > @@ -56,10 +56,10 @@ class FitImageTestCase(OESelftestTestCase): > # Define some variables which are usually defined by the kernel-fitimage.bbclass. > # But for testing purpose check if the uboot-sign.bbclass is independent from > # the kernel-fitimage.bbclass > - fit_sign_numbits = bb_vars['FIT_SIGN_NUMBITS'] or "2048" > - fit_key_genrsa_args = bb_vars['FIT_KEY_GENRSA_ARGS'] or "-F4" > - fit_key_req_args = bb_vars['FIT_KEY_REQ_ARGS'] or "-batch -new" > - fit_key_sign_pkcs = bb_vars['FIT_KEY_SIGN_PKCS'] or "-x509" > + fit_sign_numbits = bb_vars.get('FIT_SIGN_NUMBITS') or "2048" This change seems unrelated , maybe split it into preparatory patch ? > + fit_key_genrsa_args = bb_vars.get('FIT_KEY_GENRSA_ARGS') or "-F4" > + fit_key_req_args = bb_vars.get('FIT_KEY_REQ_ARGS') or "-batch -new" > + fit_key_sign_pkcs = bb_vars.get('FIT_KEY_SIGN_PKCS') or "-x509" > > uboot_sign_keydir = bb_vars['UBOOT_SIGN_KEYDIR'] > sign_keys = [bb_vars['UBOOT_SIGN_KEYNAME']] > @@ -112,6 +112,22 @@ class FitImageTestCase(OESelftestTestCase): > self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) > self.assertIn("Signature check OK", result.output) > > + def _verify_dtb_property(self, dtc_bindir, dtb_path, node_path, property_name, req_property, absent=False): > + """Verify device tree properties > + > + The fdtget utility from ftc-native is called and the property is compared. dtc-native (typo) ? > + """ > + fdtget_path = os.path.join(dtc_bindir, 'fdtget') > + cmd = '%s %s %s %s' % (fdtget_path, dtb_path, node_path, property_name) > + if absent: > + result = runCmd(cmd, ignore_status=True) > + self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) > + self.assertIn("FDT_ERR_NOTFOUND", result.output) > + else: > + result = runCmd(cmd) > + self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) > + self.assertEqual(req_property, result.output.strip()) > + > @staticmethod > def _find_string_in_bin_file(file_path, search_string): > """find strings in a binary file > @@ -145,7 +161,7 @@ class FitImageTestCase(OESelftestTestCase): > > @staticmethod > def _get_dtb_files(bb_vars): > - kernel_devicetree = bb_vars['KERNEL_DEVICETREE'] or "" > + kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE') or "" Preparatory patch change too ? > if kernel_devicetree: > return [os.path.basename(dtb) for dtb in kernel_devicetree.split()] > return [] > @@ -281,7 +297,6 @@ class FitImageTestCase(OESelftestTestCase): > key = key.strip() > value = value.strip() > except ValueError as val_err: > - self.logger.debug("dumpimage debug: %s = %s" % (key, line)) > # Handle multiple entries as e.g. for Loadables as a list > if key and line.startswith(" "): > value = sections[in_section][key] + "," + line.strip() > @@ -377,6 +392,7 @@ class KernelFitImageTests(FitImageTestCase): > 'UBOOT_SIGN_KEYNAME', > } > bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/kernel") > + self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) Preparatory patch change too ? > return bb_vars > > def _config_add_uboot_env(self, config): > @@ -877,12 +893,20 @@ class UBootFitImageTests(FitImageTestCase): > """ > internal_used = { > 'DEPLOY_DIR_IMAGE', > + 'FIT_HASH_ALG', > + 'FIT_KEY_GENRSA_ARGS', > + 'FIT_KEY_REQ_ARGS', > + 'FIT_KEY_SIGN_PKCS', > + 'FIT_SIGN_ALG', > + 'FIT_SIGN_INDIVIDUAL', > + 'FIT_SIGN_NUMBITS', > 'MACHINE', > 'SPL_MKIMAGE_SIGN_ARGS', > 'SPL_SIGN_ENABLE', > 'SPL_SIGN_KEYNAME', > 'UBOOT_ARCH', > 'UBOOT_DTB_BINARY', > + 'UBOOT_DTB_IMAGE', > 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT', > 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS', > 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE', > @@ -898,9 +922,13 @@ class UBootFitImageTests(FitImageTestCase): > 'UBOOT_FIT_USER_SETTINGS', > 'UBOOT_FITIMAGE_ENABLE', > 'UBOOT_NODTB_BINARY', > + 'UBOOT_SIGN_ENABLE', > 'UBOOT_SIGN_IMG_KEYNAME', > + 'UBOOT_SIGN_KEYDIR', > + 'UBOOT_SIGN_KEYNAME', > } > bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/bootloader") > + self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) > return bb_vars > > def _bitbake_fit_image(self, bb_vars): > @@ -1082,6 +1110,50 @@ class UBootFitImageTests(FitImageTestCase): > self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % > (num_signatures, a_comment)) > > + def _check_kernel_dtb(self, bb_vars): > + """ > + Check if the device-tree from U-Boot has the kernel public key(s). > + > + The concat_dtb function of the uboot-sign.bbclass injects the public keys > + which are required for verifying the kernel at run-time into the DTB from > + U-Boot. The following example is from a build with FIT_SIGN_INDIVIDUAL > + set to "1". If it is set to "0" the key-the-kernel-image-key node is not > + present. > + / { > + ... > + signature { > + key-the-kernel-image-key { > + required = "image"; > + algo = "sha256,rsa2048"; > + ... > + }; > + key-the-kernel-config-key { > + required = "conf"; > + algo = "sha256,rsa2048"; > + ... > + }; > + }; > + """ > + # Setup u-boot-tools-native > + dtc_bindir = FitImageTestCase._setup_native('dtc-native') > + > + # Check if 1 or 2 signature sections are in the DTB. > + uboot_dtb_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], bb_vars['UBOOT_DTB_IMAGE']) > + algo = "%s,%s" % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']) > + if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": > + uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] > + key_dtb_path = "/signature/key-" + uboot_sign_img_keyname > + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "image") > + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) > + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_img_keyname) > + > + uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] > + key_dtb_path = "/signature/key-" + uboot_sign_keyname > + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "conf") > + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) > + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_keyname) > + > + > def test_uboot_fit_image(self): > """ > Summary: Check if Uboot FIT image and Image Tree Source > @@ -1174,9 +1246,9 @@ UBOOT_FIT_HASH_ALG = "sha256" > via UBOOT_FIT_GENERATE_KEYS) > 3) Dumping the FIT image indicates signature values > are present > - 4) Examination of the do_uboot_assemble_fitimage > - runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN > - and SPL_MKIMAGE_SIGN_ARGS are working as expected. > + 4) Examination of the do_uboot_assemble_fitimage that > + UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and SPL_MKIMAGE_SIGN_ARGS > + are working as expected. Preparatory patch change too ? Sorry for the delayed reply.
diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 721628d8e73..d79c8cf321a 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -56,10 +56,10 @@ class FitImageTestCase(OESelftestTestCase): # Define some variables which are usually defined by the kernel-fitimage.bbclass. # But for testing purpose check if the uboot-sign.bbclass is independent from # the kernel-fitimage.bbclass - fit_sign_numbits = bb_vars['FIT_SIGN_NUMBITS'] or "2048" - fit_key_genrsa_args = bb_vars['FIT_KEY_GENRSA_ARGS'] or "-F4" - fit_key_req_args = bb_vars['FIT_KEY_REQ_ARGS'] or "-batch -new" - fit_key_sign_pkcs = bb_vars['FIT_KEY_SIGN_PKCS'] or "-x509" + fit_sign_numbits = bb_vars.get('FIT_SIGN_NUMBITS') or "2048" + fit_key_genrsa_args = bb_vars.get('FIT_KEY_GENRSA_ARGS') or "-F4" + fit_key_req_args = bb_vars.get('FIT_KEY_REQ_ARGS') or "-batch -new" + fit_key_sign_pkcs = bb_vars.get('FIT_KEY_SIGN_PKCS') or "-x509" uboot_sign_keydir = bb_vars['UBOOT_SIGN_KEYDIR'] sign_keys = [bb_vars['UBOOT_SIGN_KEYNAME']] @@ -112,6 +112,22 @@ class FitImageTestCase(OESelftestTestCase): self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) self.assertIn("Signature check OK", result.output) + def _verify_dtb_property(self, dtc_bindir, dtb_path, node_path, property_name, req_property, absent=False): + """Verify device tree properties + + The fdtget utility from ftc-native is called and the property is compared. + """ + fdtget_path = os.path.join(dtc_bindir, 'fdtget') + cmd = '%s %s %s %s' % (fdtget_path, dtb_path, node_path, property_name) + if absent: + result = runCmd(cmd, ignore_status=True) + self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) + self.assertIn("FDT_ERR_NOTFOUND", result.output) + else: + result = runCmd(cmd) + self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) + self.assertEqual(req_property, result.output.strip()) + @staticmethod def _find_string_in_bin_file(file_path, search_string): """find strings in a binary file @@ -145,7 +161,7 @@ class FitImageTestCase(OESelftestTestCase): @staticmethod def _get_dtb_files(bb_vars): - kernel_devicetree = bb_vars['KERNEL_DEVICETREE'] or "" + kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE') or "" if kernel_devicetree: return [os.path.basename(dtb) for dtb in kernel_devicetree.split()] return [] @@ -281,7 +297,6 @@ class FitImageTestCase(OESelftestTestCase): key = key.strip() value = value.strip() except ValueError as val_err: - self.logger.debug("dumpimage debug: %s = %s" % (key, line)) # Handle multiple entries as e.g. for Loadables as a list if key and line.startswith(" "): value = sections[in_section][key] + "," + line.strip() @@ -377,6 +392,7 @@ class KernelFitImageTests(FitImageTestCase): 'UBOOT_SIGN_KEYNAME', } bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/kernel") + self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) return bb_vars def _config_add_uboot_env(self, config): @@ -877,12 +893,20 @@ class UBootFitImageTests(FitImageTestCase): """ internal_used = { 'DEPLOY_DIR_IMAGE', + 'FIT_HASH_ALG', + 'FIT_KEY_GENRSA_ARGS', + 'FIT_KEY_REQ_ARGS', + 'FIT_KEY_SIGN_PKCS', + 'FIT_SIGN_ALG', + 'FIT_SIGN_INDIVIDUAL', + 'FIT_SIGN_NUMBITS', 'MACHINE', 'SPL_MKIMAGE_SIGN_ARGS', 'SPL_SIGN_ENABLE', 'SPL_SIGN_KEYNAME', 'UBOOT_ARCH', 'UBOOT_DTB_BINARY', + 'UBOOT_DTB_IMAGE', 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT', 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS', 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE', @@ -898,9 +922,13 @@ class UBootFitImageTests(FitImageTestCase): 'UBOOT_FIT_USER_SETTINGS', 'UBOOT_FITIMAGE_ENABLE', 'UBOOT_NODTB_BINARY', + 'UBOOT_SIGN_ENABLE', 'UBOOT_SIGN_IMG_KEYNAME', + 'UBOOT_SIGN_KEYDIR', + 'UBOOT_SIGN_KEYNAME', } bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/bootloader") + self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) return bb_vars def _bitbake_fit_image(self, bb_vars): @@ -1082,6 +1110,50 @@ class UBootFitImageTests(FitImageTestCase): self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % (num_signatures, a_comment)) + def _check_kernel_dtb(self, bb_vars): + """ + Check if the device-tree from U-Boot has the kernel public key(s). + + The concat_dtb function of the uboot-sign.bbclass injects the public keys + which are required for verifying the kernel at run-time into the DTB from + U-Boot. The following example is from a build with FIT_SIGN_INDIVIDUAL + set to "1". If it is set to "0" the key-the-kernel-image-key node is not + present. + / { + ... + signature { + key-the-kernel-image-key { + required = "image"; + algo = "sha256,rsa2048"; + ... + }; + key-the-kernel-config-key { + required = "conf"; + algo = "sha256,rsa2048"; + ... + }; + }; + """ + # Setup u-boot-tools-native + dtc_bindir = FitImageTestCase._setup_native('dtc-native') + + # Check if 1 or 2 signature sections are in the DTB. + uboot_dtb_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], bb_vars['UBOOT_DTB_IMAGE']) + algo = "%s,%s" % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']) + if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": + uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] + key_dtb_path = "/signature/key-" + uboot_sign_img_keyname + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "image") + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_img_keyname) + + uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] + key_dtb_path = "/signature/key-" + uboot_sign_keyname + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "conf") + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) + self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_keyname) + + def test_uboot_fit_image(self): """ Summary: Check if Uboot FIT image and Image Tree Source @@ -1174,9 +1246,9 @@ UBOOT_FIT_HASH_ALG = "sha256" via UBOOT_FIT_GENERATE_KEYS) 3) Dumping the FIT image indicates signature values are present - 4) Examination of the do_uboot_assemble_fitimage - runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN - and SPL_MKIMAGE_SIGN_ARGS are working as expected. + 4) Examination of the do_uboot_assemble_fitimage that + UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and SPL_MKIMAGE_SIGN_ARGS + are working as expected. Product: oe-core Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon work by Paul Eggleton <paul.eggleton@microsoft.com> and @@ -1206,15 +1278,17 @@ UBOOT_EXTLINUX = "0" UBOOT_FIT_GENERATE_KEYS = "1" UBOOT_FIT_HASH_ALG = "sha256" UBOOT_SIGN_ENABLE = "1" -FIT_GENERATE_KEYS = "1" UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" -UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" -FIT_SIGN_INDIVIDUAL = "1" """ self.write_config(config) bb_vars = self._fit_get_bb_vars() + + # Using a static key. FIT_GENERATE_KEYS = "1" does not work without kernel-fitimage.bbclass + self._gen_signing_key(bb_vars) + self._test_fitimage(bb_vars) + self._check_kernel_dtb(bb_vars) def test_uboot_atf_tee_fit_image(self): """ @@ -1349,3 +1423,35 @@ UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" FitImageTestCase._gen_random_file(dummy_tee) self._test_fitimage(bb_vars) + + + def test_sign_uboot_kernel_individual(self): + """ + Summary: Check if the device-tree from U-Boot has two public keys + for verifying the kernel FIT image created by the + kernel-fitimage.bbclass included. + This test sets: FIT_SIGN_INDIVIDUAL = "1" + Expected: There must be two signature nodes. One is required for + the individual image nodes, the other is required for the + verification of the configuration section. + """ + config = """ +# Enable creation of fitImage +MACHINE = "beaglebone-yocto" +UBOOT_SIGN_ENABLE = "1" +UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" +UBOOT_SIGN_KEYNAME = "the-kernel-config-key" +UBOOT_SIGN_IMG_KEYNAME = "the-kernel-image-key" +UBOOT_MKIMAGE_DTCOPTS="-I dts -O dtb -p 2000" +FIT_SIGN_INDIVIDUAL = "1" +""" + self.write_config(config) + bb_vars = self._fit_get_bb_vars() + + # Using a static key. FIT_GENERATE_KEYS = "1" does not work without kernel-fitimage.bbclass + self._gen_signing_key(bb_vars) + + bitbake("virtual/bootloader") + + # Just check the DTB of u-boot since there is no u-boot FIT image + self._check_kernel_dtb(bb_vars)
Add a new test function which checks that the device-tree of U-Boot contains the public keys which are required for checking the signature of the kernel FIT image at run-time. Use this new _check_kernel_dtb function in the existing test_sign_cascaded_uboot_fit_image test case which already creates a build configuration with UBOOT_SIGN_ENABLE = "1" and keys for the kernel. But so far there was no check that the keys for the kernel verification got added to U-Boot's DTB. This test case checks the configuration where only the configuration nodes of the kernel FIT image are signed. A new test case test_sign_uboot_kernel_individual checks the configuration with two keys and signed image and signed configuration nodes. This test case covers the use case which recently broke with commit: OE-Core rev: 259bfa86f384206f0d0a96a5b84887186c5f689e u-boot: kernel-fitimage: Fix dependency loop if UBOOT_SIGN_ENABLE and UBOOT_ENV enabled and got fixed with commit OE-Core rev: 0106e5efab99c8016836a2ab71e2327ce58a9a9d u-boot: kernel-fitimage: Restore FIT_SIGN_INDIVIDUAL="1" behavior This patch also fixes a few more details: - Use the get function to access the bb_var dicts where appropriate. - log bb_vars if oe-selftest is called with -v - Simplify the code by moving all the U-Boot related variables to the _fit_get_bb_vars function. - Do not set FIT_GENERATE_KEYS = "1" without inheriting the kernel-fitimage.bbclass which handles this variable. Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com> --- meta/lib/oeqa/selftest/cases/fitimage.py | 130 ++++++++++++++++++++--- 1 file changed, 118 insertions(+), 12 deletions(-)