xref: /rk3399_rockchip-uboot/test/py/tests/test_vboot.py (revision 8729d582595dc0a9a666310f9f6001f815684f73)
1*8729d582SSimon Glass# Copyright (c) 2013, Google Inc.
2*8729d582SSimon Glass#
3*8729d582SSimon Glass# SPDX-License-Identifier:	GPL-2.0+
4*8729d582SSimon Glass#
5*8729d582SSimon Glass# U-Boot Verified Boot Test
6*8729d582SSimon Glass
7*8729d582SSimon Glass"""
8*8729d582SSimon GlassThis tests verified boot in the following ways:
9*8729d582SSimon Glass
10*8729d582SSimon GlassFor image verification:
11*8729d582SSimon Glass- Create FIT (unsigned) with mkimage
12*8729d582SSimon Glass- Check that verification shows that no keys are verified
13*8729d582SSimon Glass- Sign image
14*8729d582SSimon Glass- Check that verification shows that a key is now verified
15*8729d582SSimon Glass
16*8729d582SSimon GlassFor configuration verification:
17*8729d582SSimon Glass- Corrupt signature and check for failure
18*8729d582SSimon Glass- Create FIT (with unsigned configuration) with mkimage
19*8729d582SSimon Glass- Check that image veriication works
20*8729d582SSimon Glass- Sign the FIT and mark the key as 'required' for verification
21*8729d582SSimon Glass- Check that image verification works
22*8729d582SSimon Glass- Corrupt the signature
23*8729d582SSimon Glass- Check that image verification no-longer works
24*8729d582SSimon Glass
25*8729d582SSimon GlassTests run with both SHA1 and SHA256 hashing.
26*8729d582SSimon Glass"""
27*8729d582SSimon Glass
28*8729d582SSimon Glassimport pytest
29*8729d582SSimon Glassimport sys
30*8729d582SSimon Glassimport u_boot_utils as util
31*8729d582SSimon Glass
32*8729d582SSimon Glass@pytest.mark.buildconfigspec('fit_signature')
33*8729d582SSimon Glassdef test_vboot(u_boot_console):
34*8729d582SSimon Glass    """Test verified boot signing with mkimage and verification with 'bootm'.
35*8729d582SSimon Glass
36*8729d582SSimon Glass    This works using sandbox only as it needs to update the device tree used
37*8729d582SSimon Glass    by U-Boot to hold public keys from the signing process.
38*8729d582SSimon Glass
39*8729d582SSimon Glass    The SHA1 and SHA256 tests are combined into a single test since the
40*8729d582SSimon Glass    key-generation process is quite slow and we want to avoid doing it twice.
41*8729d582SSimon Glass    """
42*8729d582SSimon Glass    def dtc(dts):
43*8729d582SSimon Glass        """Run the device-tree compiler to compile a .dts file
44*8729d582SSimon Glass
45*8729d582SSimon Glass        The output file will be the same as the input file but with a .dtb
46*8729d582SSimon Glass        extension.
47*8729d582SSimon Glass
48*8729d582SSimon Glass        Args:
49*8729d582SSimon Glass            dts: Device tree file to compile.
50*8729d582SSimon Glass        """
51*8729d582SSimon Glass        dtb = dts.replace('.dts', '.dtb')
52*8729d582SSimon Glass        util.cmd(cons, 'dtc %s %s%s -O dtb '
53*8729d582SSimon Glass                       '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
54*8729d582SSimon Glass
55*8729d582SSimon Glass    def run_bootm(test_type, expect_string):
56*8729d582SSimon Glass        """Run a 'bootm' command U-Boot.
57*8729d582SSimon Glass
58*8729d582SSimon Glass        This always starts a fresh U-Boot instance since the device tree may
59*8729d582SSimon Glass        contain a new public key.
60*8729d582SSimon Glass
61*8729d582SSimon Glass        Args:
62*8729d582SSimon Glass            test_type: A string identifying the test type
63*8729d582SSimon Glass            expect_string: A string which is expected in the output
64*8729d582SSimon Glass        """
65*8729d582SSimon Glass        cons.cleanup_spawn()
66*8729d582SSimon Glass        cons.ensure_spawned()
67*8729d582SSimon Glass        cons.log.action('%s: Test Verified Boot Run: %s' % (algo, test_type))
68*8729d582SSimon Glass        output = cons.run_command_list(
69*8729d582SSimon Glass            ['sb load hostfs - 100 %stest.fit' % tmpdir,
70*8729d582SSimon Glass             'fdt addr 100',
71*8729d582SSimon Glass             'bootm 100'])
72*8729d582SSimon Glass        assert(expect_string in output)
73*8729d582SSimon Glass
74*8729d582SSimon Glass    def make_fit(its):
75*8729d582SSimon Glass        """Make a new FIT from the .its source file
76*8729d582SSimon Glass
77*8729d582SSimon Glass        This runs 'mkimage -f' to create a new FIT.
78*8729d582SSimon Glass
79*8729d582SSimon Glass        Args:
80*8729d582SSimon Glass            its: Filename containing .its source
81*8729d582SSimon Glass        """
82*8729d582SSimon Glass        util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f',
83*8729d582SSimon Glass                                '%s%s' % (datadir, its), fit])
84*8729d582SSimon Glass
85*8729d582SSimon Glass    def sign_fit():
86*8729d582SSimon Glass        """Sign the FIT
87*8729d582SSimon Glass
88*8729d582SSimon Glass        Signs the FIT and writes the signature into it. It also writes the
89*8729d582SSimon Glass        public key into the dtb.
90*8729d582SSimon Glass        """
91*8729d582SSimon Glass        cons.log.action('%s: Sign images' % algo)
92*8729d582SSimon Glass        util.run_and_log(cons, [mkimage, '-F', '-k', tmpdir, '-K', dtb,
93*8729d582SSimon Glass                                '-r', fit])
94*8729d582SSimon Glass
95*8729d582SSimon Glass    def test_with_algo(sha):
96*8729d582SSimon Glass        """Test verified boot with the given hash algorithm
97*8729d582SSimon Glass
98*8729d582SSimon Glass        This is the main part of the test code. The same procedure is followed
99*8729d582SSimon Glass        for both hashing algorithms.
100*8729d582SSimon Glass
101*8729d582SSimon Glass        Args:
102*8729d582SSimon Glass            sha: Either 'sha1' or 'sha256', to select the algorithm to use
103*8729d582SSimon Glass        """
104*8729d582SSimon Glass        global algo
105*8729d582SSimon Glass
106*8729d582SSimon Glass        algo = sha
107*8729d582SSimon Glass
108*8729d582SSimon Glass        # Compile our device tree files for kernel and U-Boot
109*8729d582SSimon Glass        dtc('sandbox-kernel.dts')
110*8729d582SSimon Glass        dtc('sandbox-u-boot.dts')
111*8729d582SSimon Glass
112*8729d582SSimon Glass        # Build the FIT, but don't sign anything yet
113*8729d582SSimon Glass        cons.log.action('%s: Test FIT with signed images' % algo)
114*8729d582SSimon Glass        make_fit('sign-images-%s.its' % algo)
115*8729d582SSimon Glass        run_bootm('unsigned images', 'dev-')
116*8729d582SSimon Glass
117*8729d582SSimon Glass        # Sign images with our dev keys
118*8729d582SSimon Glass        sign_fit()
119*8729d582SSimon Glass        run_bootm('signed images', 'dev+')
120*8729d582SSimon Glass
121*8729d582SSimon Glass        # Create a fresh .dtb without the public keys
122*8729d582SSimon Glass        dtc('sandbox-u-boot.dts')
123*8729d582SSimon Glass
124*8729d582SSimon Glass        cons.log.action('%s: Test FIT with signed configuration' % algo)
125*8729d582SSimon Glass        make_fit('sign-configs-%s.its' % algo)
126*8729d582SSimon Glass        run_bootm('unsigned config', '%s+ OK' % algo)
127*8729d582SSimon Glass
128*8729d582SSimon Glass        # Sign images with our dev keys
129*8729d582SSimon Glass        sign_fit()
130*8729d582SSimon Glass        run_bootm('signed config', 'dev+')
131*8729d582SSimon Glass
132*8729d582SSimon Glass        cons.log.action('%s: Check signed config on the host' % algo)
133*8729d582SSimon Glass
134*8729d582SSimon Glass        util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', tmpdir,
135*8729d582SSimon Glass                                '-k', dtb])
136*8729d582SSimon Glass
137*8729d582SSimon Glass        # Increment the first byte of the signature, which should cause failure
138*8729d582SSimon Glass        sig = util.cmd(cons, 'fdtget -t bx %s %s value' % (fit, sig_node))
139*8729d582SSimon Glass        byte_list = sig.split()
140*8729d582SSimon Glass        byte = int(byte_list[0], 16)
141*8729d582SSimon Glass        byte_list = ['%x' % (byte + 1)] + byte_list[1:]
142*8729d582SSimon Glass        sig = ' '.join(byte_list)
143*8729d582SSimon Glass        util.cmd(cons, 'fdtput -t bx %s %s value %s' % (fit, sig_node, sig))
144*8729d582SSimon Glass
145*8729d582SSimon Glass        run_bootm('Signed config with bad hash', 'Bad Data Hash')
146*8729d582SSimon Glass
147*8729d582SSimon Glass        cons.log.action('%s: Check bad config on the host' % algo)
148*8729d582SSimon Glass        util.run_and_log_expect_exception(cons, [fit_check_sign, '-f', fit,
149*8729d582SSimon Glass                '-k', dtb], 1, 'Failed to verify required signature')
150*8729d582SSimon Glass
151*8729d582SSimon Glass    cons = u_boot_console
152*8729d582SSimon Glass    tmpdir = cons.config.result_dir + '/'
153*8729d582SSimon Glass    tmp = tmpdir + 'vboot.tmp'
154*8729d582SSimon Glass    datadir = 'test/py/tests/vboot/'
155*8729d582SSimon Glass    fit = '%stest.fit' % tmpdir
156*8729d582SSimon Glass    mkimage = cons.config.build_dir + '/tools/mkimage'
157*8729d582SSimon Glass    fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
158*8729d582SSimon Glass    dtc_args = '-I dts -O dtb -i %s' % tmpdir
159*8729d582SSimon Glass    dtb = '%ssandbox-u-boot.dtb' % tmpdir
160*8729d582SSimon Glass    sig_node = '/configurations/conf@1/signature@1'
161*8729d582SSimon Glass
162*8729d582SSimon Glass    # Create an RSA key pair
163*8729d582SSimon Glass    public_exponent = 65537
164*8729d582SSimon Glass    util.cmd(cons, 'openssl genpkey -algorithm RSA -out %sdev.key '
165*8729d582SSimon Glass                   '-pkeyopt rsa_keygen_bits:2048 '
166*8729d582SSimon Glass                   '-pkeyopt rsa_keygen_pubexp:%d '
167*8729d582SSimon Glass                   '2>/dev/null'  % (tmpdir, public_exponent))
168*8729d582SSimon Glass
169*8729d582SSimon Glass    # Create a certificate containing the public key
170*8729d582SSimon Glass    util.cmd(cons, 'openssl req -batch -new -x509 -key %sdev.key -out '
171*8729d582SSimon Glass                   '%sdev.crt' % (tmpdir, tmpdir))
172*8729d582SSimon Glass
173*8729d582SSimon Glass    # Create a number kernel image with zeroes
174*8729d582SSimon Glass    with open('%stest-kernel.bin' % tmpdir, 'w') as fd:
175*8729d582SSimon Glass        fd.write(5000 * chr(0))
176*8729d582SSimon Glass
177*8729d582SSimon Glass    try:
178*8729d582SSimon Glass        # We need to use our own device tree file. Remember to restore it
179*8729d582SSimon Glass        # afterwards.
180*8729d582SSimon Glass        old_dtb = cons.config.dtb
181*8729d582SSimon Glass        cons.config.dtb = dtb
182*8729d582SSimon Glass        test_with_algo('sha1')
183*8729d582SSimon Glass        test_with_algo('sha256')
184*8729d582SSimon Glass    finally:
185*8729d582SSimon Glass        cons.config.dtb = old_dtb
186