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