172f52268SSimon Glass# Copyright (c) 2016, Google Inc. 28729d582SSimon Glass# 38729d582SSimon Glass# SPDX-License-Identifier: GPL-2.0+ 48729d582SSimon Glass# 58729d582SSimon Glass# U-Boot Verified Boot Test 68729d582SSimon Glass 78729d582SSimon Glass""" 88729d582SSimon GlassThis tests verified boot in the following ways: 98729d582SSimon Glass 108729d582SSimon GlassFor image verification: 118729d582SSimon Glass- Create FIT (unsigned) with mkimage 128729d582SSimon Glass- Check that verification shows that no keys are verified 138729d582SSimon Glass- Sign image 148729d582SSimon Glass- Check that verification shows that a key is now verified 158729d582SSimon Glass 168729d582SSimon GlassFor configuration verification: 178729d582SSimon Glass- Corrupt signature and check for failure 188729d582SSimon Glass- Create FIT (with unsigned configuration) with mkimage 1972f52268SSimon Glass- Check that image verification works 208729d582SSimon Glass- Sign the FIT and mark the key as 'required' for verification 218729d582SSimon Glass- Check that image verification works 228729d582SSimon Glass- Corrupt the signature 238729d582SSimon Glass- Check that image verification no-longer works 248729d582SSimon Glass 258729d582SSimon GlassTests run with both SHA1 and SHA256 hashing. 268729d582SSimon Glass""" 278729d582SSimon Glass 288729d582SSimon Glassimport pytest 298729d582SSimon Glassimport sys 308729d582SSimon Glassimport u_boot_utils as util 318729d582SSimon Glass 3204a4786cSMichal Simek@pytest.mark.boardspec('sandbox') 338729d582SSimon Glass@pytest.mark.buildconfigspec('fit_signature') 348729d582SSimon Glassdef test_vboot(u_boot_console): 358729d582SSimon Glass """Test verified boot signing with mkimage and verification with 'bootm'. 368729d582SSimon Glass 378729d582SSimon Glass This works using sandbox only as it needs to update the device tree used 388729d582SSimon Glass by U-Boot to hold public keys from the signing process. 398729d582SSimon Glass 408729d582SSimon Glass The SHA1 and SHA256 tests are combined into a single test since the 418729d582SSimon Glass key-generation process is quite slow and we want to avoid doing it twice. 428729d582SSimon Glass """ 438729d582SSimon Glass def dtc(dts): 4472f52268SSimon Glass """Run the device tree compiler to compile a .dts file 458729d582SSimon Glass 468729d582SSimon Glass The output file will be the same as the input file but with a .dtb 478729d582SSimon Glass extension. 488729d582SSimon Glass 498729d582SSimon Glass Args: 508729d582SSimon Glass dts: Device tree file to compile. 518729d582SSimon Glass """ 528729d582SSimon Glass dtb = dts.replace('.dts', '.dtb') 53ec70f8a9SSimon Glass util.run_and_log(cons, 'dtc %s %s%s -O dtb ' 548729d582SSimon Glass '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb)) 558729d582SSimon Glass 56*de4be9ecSTom Rini def run_bootm(sha_algo, test_type, expect_string, boots): 578729d582SSimon Glass """Run a 'bootm' command U-Boot. 588729d582SSimon Glass 598729d582SSimon Glass This always starts a fresh U-Boot instance since the device tree may 608729d582SSimon Glass contain a new public key. 618729d582SSimon Glass 628729d582SSimon Glass Args: 63ac9a23cfSSimon Glass test_type: A string identifying the test type. 64ac9a23cfSSimon Glass expect_string: A string which is expected in the output. 65ac9a23cfSSimon Glass sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 66ac9a23cfSSimon Glass use. 67*de4be9ecSTom Rini boots: A boolean that is True if Linux should boot and False if 68*de4be9ecSTom Rini we are expected to not boot 698729d582SSimon Glass """ 7027c087d5SSimon Glass cons.restart_uboot() 71851271a7SSimon Glass with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)): 728729d582SSimon Glass output = cons.run_command_list( 738729d582SSimon Glass ['sb load hostfs - 100 %stest.fit' % tmpdir, 748729d582SSimon Glass 'fdt addr 100', 758729d582SSimon Glass 'bootm 100']) 76f6d34651SSimon Glass assert(expect_string in ''.join(output)) 77*de4be9ecSTom Rini if boots: 78*de4be9ecSTom Rini assert('sandbox: continuing, as we cannot run' in ''.join(output)) 798729d582SSimon Glass 808729d582SSimon Glass def make_fit(its): 8172f52268SSimon Glass """Make a new FIT from the .its source file. 828729d582SSimon Glass 838729d582SSimon Glass This runs 'mkimage -f' to create a new FIT. 848729d582SSimon Glass 858729d582SSimon Glass Args: 8672f52268SSimon Glass its: Filename containing .its source. 878729d582SSimon Glass """ 888729d582SSimon Glass util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', 898729d582SSimon Glass '%s%s' % (datadir, its), fit]) 908729d582SSimon Glass 91ac9a23cfSSimon Glass def sign_fit(sha_algo): 928729d582SSimon Glass """Sign the FIT 938729d582SSimon Glass 948729d582SSimon Glass Signs the FIT and writes the signature into it. It also writes the 958729d582SSimon Glass public key into the dtb. 96ac9a23cfSSimon Glass 97ac9a23cfSSimon Glass Args: 98ac9a23cfSSimon Glass sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 99ac9a23cfSSimon Glass use. 1008729d582SSimon Glass """ 101ac9a23cfSSimon Glass cons.log.action('%s: Sign images' % sha_algo) 1028729d582SSimon Glass util.run_and_log(cons, [mkimage, '-F', '-k', tmpdir, '-K', dtb, 1038729d582SSimon Glass '-r', fit]) 1048729d582SSimon Glass 105ac9a23cfSSimon Glass def test_with_algo(sha_algo): 10672f52268SSimon Glass """Test verified boot with the given hash algorithm. 1078729d582SSimon Glass 1088729d582SSimon Glass This is the main part of the test code. The same procedure is followed 1098729d582SSimon Glass for both hashing algorithms. 1108729d582SSimon Glass 1118729d582SSimon Glass Args: 112ac9a23cfSSimon Glass sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 113ac9a23cfSSimon Glass use. 1148729d582SSimon Glass """ 115bcbd0c8fSSimon Glass # Compile our device tree files for kernel and U-Boot. These are 116bcbd0c8fSSimon Glass # regenerated here since mkimage will modify them (by adding a 117bcbd0c8fSSimon Glass # public key) below. 1188729d582SSimon Glass dtc('sandbox-kernel.dts') 1198729d582SSimon Glass dtc('sandbox-u-boot.dts') 1208729d582SSimon Glass 1218729d582SSimon Glass # Build the FIT, but don't sign anything yet 122ac9a23cfSSimon Glass cons.log.action('%s: Test FIT with signed images' % sha_algo) 123ac9a23cfSSimon Glass make_fit('sign-images-%s.its' % sha_algo) 124*de4be9ecSTom Rini run_bootm(sha_algo, 'unsigned images', 'dev-', True) 1258729d582SSimon Glass 1268729d582SSimon Glass # Sign images with our dev keys 127ac9a23cfSSimon Glass sign_fit(sha_algo) 128*de4be9ecSTom Rini run_bootm(sha_algo, 'signed images', 'dev+', True) 1298729d582SSimon Glass 1308729d582SSimon Glass # Create a fresh .dtb without the public keys 1318729d582SSimon Glass dtc('sandbox-u-boot.dts') 1328729d582SSimon Glass 133ac9a23cfSSimon Glass cons.log.action('%s: Test FIT with signed configuration' % sha_algo) 134ac9a23cfSSimon Glass make_fit('sign-configs-%s.its' % sha_algo) 135*de4be9ecSTom Rini run_bootm(sha_algo, 'unsigned config', '%s+ OK' % sha_algo, True) 1368729d582SSimon Glass 1378729d582SSimon Glass # Sign images with our dev keys 138ac9a23cfSSimon Glass sign_fit(sha_algo) 139*de4be9ecSTom Rini run_bootm(sha_algo, 'signed config', 'dev+', True) 1408729d582SSimon Glass 141ac9a23cfSSimon Glass cons.log.action('%s: Check signed config on the host' % sha_algo) 1428729d582SSimon Glass 1438729d582SSimon Glass util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', tmpdir, 1448729d582SSimon Glass '-k', dtb]) 1458729d582SSimon Glass 1468729d582SSimon Glass # Increment the first byte of the signature, which should cause failure 147ec70f8a9SSimon Glass sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' % 148ec70f8a9SSimon Glass (fit, sig_node)) 1498729d582SSimon Glass byte_list = sig.split() 1508729d582SSimon Glass byte = int(byte_list[0], 16) 151bcbd0c8fSSimon Glass byte_list[0] = '%x' % (byte + 1) 1528729d582SSimon Glass sig = ' '.join(byte_list) 153ec70f8a9SSimon Glass util.run_and_log(cons, 'fdtput -t bx %s %s value %s' % 154ec70f8a9SSimon Glass (fit, sig_node, sig)) 1558729d582SSimon Glass 156*de4be9ecSTom Rini run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', False) 1578729d582SSimon Glass 158ac9a23cfSSimon Glass cons.log.action('%s: Check bad config on the host' % sha_algo) 1598729d582SSimon Glass util.run_and_log_expect_exception(cons, [fit_check_sign, '-f', fit, 1608729d582SSimon Glass '-k', dtb], 1, 'Failed to verify required signature') 1618729d582SSimon Glass 1628729d582SSimon Glass cons = u_boot_console 1638729d582SSimon Glass tmpdir = cons.config.result_dir + '/' 1648729d582SSimon Glass tmp = tmpdir + 'vboot.tmp' 165c9ba60c4SStephen Warren datadir = cons.config.source_dir + '/test/py/tests/vboot/' 1668729d582SSimon Glass fit = '%stest.fit' % tmpdir 1678729d582SSimon Glass mkimage = cons.config.build_dir + '/tools/mkimage' 1688729d582SSimon Glass fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' 1698729d582SSimon Glass dtc_args = '-I dts -O dtb -i %s' % tmpdir 1708729d582SSimon Glass dtb = '%ssandbox-u-boot.dtb' % tmpdir 1718729d582SSimon Glass sig_node = '/configurations/conf@1/signature@1' 1728729d582SSimon Glass 1738729d582SSimon Glass # Create an RSA key pair 1748729d582SSimon Glass public_exponent = 65537 175ec70f8a9SSimon Glass util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %sdev.key ' 1768729d582SSimon Glass '-pkeyopt rsa_keygen_bits:2048 ' 1778729d582SSimon Glass '-pkeyopt rsa_keygen_pubexp:%d ' 1788729d582SSimon Glass '2>/dev/null' % (tmpdir, public_exponent)) 1798729d582SSimon Glass 1808729d582SSimon Glass # Create a certificate containing the public key 181ec70f8a9SSimon Glass util.run_and_log(cons, 'openssl req -batch -new -x509 -key %sdev.key -out ' 1828729d582SSimon Glass '%sdev.crt' % (tmpdir, tmpdir)) 1838729d582SSimon Glass 1848729d582SSimon Glass # Create a number kernel image with zeroes 1858729d582SSimon Glass with open('%stest-kernel.bin' % tmpdir, 'w') as fd: 1868729d582SSimon Glass fd.write(5000 * chr(0)) 1878729d582SSimon Glass 1888729d582SSimon Glass try: 1898729d582SSimon Glass # We need to use our own device tree file. Remember to restore it 1908729d582SSimon Glass # afterwards. 1918729d582SSimon Glass old_dtb = cons.config.dtb 1928729d582SSimon Glass cons.config.dtb = dtb 1938729d582SSimon Glass test_with_algo('sha1') 1948729d582SSimon Glass test_with_algo('sha256') 1958729d582SSimon Glass finally: 19627c087d5SSimon Glass # Go back to the original U-Boot with the correct dtb. 1978729d582SSimon Glass cons.config.dtb = old_dtb 19827c087d5SSimon Glass cons.restart_uboot() 199