]> Git Repo - J-u-boot.git/blame - test/py/tests/test_vboot.py
Merge tag 'u-boot-rockchip-20200501' of https://gitlab.denx.de/u-boot/custodians...
[J-u-boot.git] / test / py / tests / test_vboot.py
CommitLineData
8729d582 1# SPDX-License-Identifier: GPL-2.0+
83d290c5 2# Copyright (c) 2016, Google Inc.
8729d582
SG
3#
4# U-Boot Verified Boot Test
5
6"""
7This tests verified boot in the following ways:
8
9For image verification:
10- Create FIT (unsigned) with mkimage
11- Check that verification shows that no keys are verified
12- Sign image
13- Check that verification shows that a key is now verified
14
15For configuration verification:
16- Corrupt signature and check for failure
17- Create FIT (with unsigned configuration) with mkimage
72f52268 18- Check that image verification works
8729d582
SG
19- Sign the FIT and mark the key as 'required' for verification
20- Check that image verification works
21- Corrupt the signature
22- Check that image verification no-longer works
23
24Tests run with both SHA1 and SHA256 hashing.
25"""
26
72239fc8 27import struct
b008677d 28import pytest
8729d582 29import u_boot_utils as util
c021971e 30import vboot_forge
8729d582 31
1b090032 32TESTDATA = [
eb7690e8
PR
33 ['sha1', '', None, False],
34 ['sha1', '', '-E -p 0x10000', False],
35 ['sha1', '-pss', None, False],
36 ['sha1', '-pss', '-E -p 0x10000', False],
37 ['sha256', '', None, False],
38 ['sha256', '', '-E -p 0x10000', False],
39 ['sha256', '-pss', None, False],
40 ['sha256', '-pss', '-E -p 0x10000', False],
41 ['sha256', '-pss', None, True],
42 ['sha256', '-pss', '-E -p 0x10000', True],
1b090032
SG
43]
44
04a4786c 45@pytest.mark.boardspec('sandbox')
8729d582 46@pytest.mark.buildconfigspec('fit_signature')
2d26bf6c
SW
47@pytest.mark.requiredtool('dtc')
48@pytest.mark.requiredtool('fdtget')
49@pytest.mark.requiredtool('fdtput')
50@pytest.mark.requiredtool('openssl')
eb7690e8
PR
51@pytest.mark.parametrize("sha_algo,padding,sign_options,required", TESTDATA)
52def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
8729d582
SG
53 """Test verified boot signing with mkimage and verification with 'bootm'.
54
55 This works using sandbox only as it needs to update the device tree used
56 by U-Boot to hold public keys from the signing process.
57
58 The SHA1 and SHA256 tests are combined into a single test since the
59 key-generation process is quite slow and we want to avoid doing it twice.
60 """
61 def dtc(dts):
72f52268 62 """Run the device tree compiler to compile a .dts file
8729d582
SG
63
64 The output file will be the same as the input file but with a .dtb
65 extension.
66
67 Args:
68 dts: Device tree file to compile.
69 """
70 dtb = dts.replace('.dts', '.dtb')
ec70f8a9
SG
71 util.run_and_log(cons, 'dtc %s %s%s -O dtb '
72 '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
8729d582 73
de4be9ec 74 def run_bootm(sha_algo, test_type, expect_string, boots):
8729d582
SG
75 """Run a 'bootm' command U-Boot.
76
77 This always starts a fresh U-Boot instance since the device tree may
78 contain a new public key.
79
80 Args:
ac9a23cf
SG
81 test_type: A string identifying the test type.
82 expect_string: A string which is expected in the output.
83 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
84 use.
de4be9ec
TR
85 boots: A boolean that is True if Linux should boot and False if
86 we are expected to not boot
8729d582 87 """
27c087d5 88 cons.restart_uboot()
851271a7
SG
89 with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)):
90 output = cons.run_command_list(
6d07d63d 91 ['host load hostfs - 100 %stest.fit' % tmpdir,
b008677d
SG
92 'fdt addr 100',
93 'bootm 100'])
94 assert expect_string in ''.join(output)
de4be9ec 95 if boots:
b008677d 96 assert 'sandbox: continuing, as we cannot run' in ''.join(output)
ce5172cf 97 else:
3156ee35
SG
98 assert('sandbox: continuing, as we cannot run'
99 not in ''.join(output))
8729d582
SG
100
101 def make_fit(its):
72f52268 102 """Make a new FIT from the .its source file.
8729d582
SG
103
104 This runs 'mkimage -f' to create a new FIT.
105
106 Args:
72f52268 107 its: Filename containing .its source.
8729d582
SG
108 """
109 util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f',
110 '%s%s' % (datadir, its), fit])
111
eb7690e8 112 def sign_fit(sha_algo, options):
8729d582
SG
113 """Sign the FIT
114
115 Signs the FIT and writes the signature into it. It also writes the
116 public key into the dtb.
ac9a23cf
SG
117
118 Args:
119 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
120 use.
eb7690e8 121 options: Options to provide to mkimage.
8729d582 122 """
eb7690e8
PR
123 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
124 if options:
125 args += options.split(' ')
ac9a23cf 126 cons.log.action('%s: Sign images' % sha_algo)
eb7690e8 127 util.run_and_log(cons, args)
8729d582 128
72239fc8
TR
129 def replace_fit_totalsize(size):
130 """Replace FIT header's totalsize with something greater.
131
132 The totalsize must be less than or equal to FIT_SIGNATURE_MAX_SIZE.
133 If the size is greater, the signature verification should return false.
134
135 Args:
136 size: The new totalsize of the header
137
138 Returns:
139 prev_size: The previous totalsize read from the header
140 """
141 total_size = 0
142 with open(fit, 'r+b') as handle:
143 handle.seek(4)
144 total_size = handle.read(4)
145 handle.seek(4)
146 handle.write(struct.pack(">I", size))
147 return struct.unpack(">I", total_size)[0]
148
da76ed27
SG
149 def create_rsa_pair(name):
150 """Generate a new RSA key paid and certificate
151
152 Args:
153 name: Name of of the key (e.g. 'dev')
154 """
155 public_exponent = 65537
156 util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key '
157 '-pkeyopt rsa_keygen_bits:2048 '
158 '-pkeyopt rsa_keygen_pubexp:%d' %
159 (tmpdir, name, public_exponent))
160
161 # Create a certificate containing the public key
162 util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key '
163 '-out %s%s.crt' % (tmpdir, name, tmpdir, name))
164
eb7690e8 165 def test_with_algo(sha_algo, padding, sign_options):
72f52268 166 """Test verified boot with the given hash algorithm.
8729d582
SG
167
168 This is the main part of the test code. The same procedure is followed
169 for both hashing algorithms.
170
171 Args:
ac9a23cf
SG
172 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
173 use.
eb7690e8
PR
174 padding: Either '' or '-pss', to select the padding to use for the
175 rsa signature algorithm.
176 sign_options: Options to mkimage when signing a fit image.
8729d582 177 """
bcbd0c8f
SG
178 # Compile our device tree files for kernel and U-Boot. These are
179 # regenerated here since mkimage will modify them (by adding a
180 # public key) below.
8729d582
SG
181 dtc('sandbox-kernel.dts')
182 dtc('sandbox-u-boot.dts')
183
184 # Build the FIT, but don't sign anything yet
ac9a23cf 185 cons.log.action('%s: Test FIT with signed images' % sha_algo)
b008677d 186 make_fit('sign-images-%s%s.its' % (sha_algo, padding))
de4be9ec 187 run_bootm(sha_algo, 'unsigned images', 'dev-', True)
8729d582
SG
188
189 # Sign images with our dev keys
eb7690e8 190 sign_fit(sha_algo, sign_options)
de4be9ec 191 run_bootm(sha_algo, 'signed images', 'dev+', True)
8729d582
SG
192
193 # Create a fresh .dtb without the public keys
194 dtc('sandbox-u-boot.dts')
195
ac9a23cf 196 cons.log.action('%s: Test FIT with signed configuration' % sha_algo)
b008677d 197 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
de4be9ec 198 run_bootm(sha_algo, 'unsigned config', '%s+ OK' % sha_algo, True)
8729d582
SG
199
200 # Sign images with our dev keys
eb7690e8 201 sign_fit(sha_algo, sign_options)
de4be9ec 202 run_bootm(sha_algo, 'signed config', 'dev+', True)
8729d582 203
ac9a23cf 204 cons.log.action('%s: Check signed config on the host' % sha_algo)
8729d582 205
477f559e 206 util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
8729d582 207
c021971e
SG
208 # Make sure that U-Boot checks that the config is in the list of hashed
209 # nodes. If it isn't, a security bypass is possible.
b008677d
SG
210 with open(fit, 'rb') as fd:
211 root, strblock = vboot_forge.read_fdt(fd)
c021971e 212 root, strblock = vboot_forge.manipulate(root, strblock)
b008677d
SG
213 with open(fit, 'w+b') as fd:
214 vboot_forge.write_fdt(root, strblock, fd)
215 util.run_and_log_expect_exception(
216 cons, [fit_check_sign, '-f', fit, '-k', dtb],
217 1, 'Failed to verify required signature')
c021971e
SG
218
219 run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False)
220
221 # Create a new properly signed fit and replace header bytes
222 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
eb7690e8 223 sign_fit(sha_algo, sign_options)
72239fc8
TR
224 bcfg = u_boot_console.config.buildconfig
225 max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0)
226 existing_size = replace_fit_totalsize(max_size + 1)
3156ee35
SG
227 run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
228 False)
72239fc8
TR
229 cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo)
230
231 # Replace with existing header bytes
232 replace_fit_totalsize(existing_size)
233 run_bootm(sha_algo, 'signed config', 'dev+', True)
234 cons.log.action('%s: Check default FIT header totalsize' % sha_algo)
235
8729d582 236 # Increment the first byte of the signature, which should cause failure
ec70f8a9
SG
237 sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' %
238 (fit, sig_node))
8729d582
SG
239 byte_list = sig.split()
240 byte = int(byte_list[0], 16)
bcbd0c8f 241 byte_list[0] = '%x' % (byte + 1)
8729d582 242 sig = ' '.join(byte_list)
ec70f8a9
SG
243 util.run_and_log(cons, 'fdtput -t bx %s %s value %s' %
244 (fit, sig_node, sig))
8729d582 245
3156ee35
SG
246 run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
247 False)
8729d582 248
ac9a23cf 249 cons.log.action('%s: Check bad config on the host' % sha_algo)
b008677d
SG
250 util.run_and_log_expect_exception(
251 cons, [fit_check_sign, '-f', fit, '-k', dtb],
252 1, 'Failed to verify required signature')
8729d582 253
eb7690e8 254 def test_required_key(sha_algo, padding, sign_options):
ce5172cf
PR
255 """Test verified boot with the given hash algorithm.
256
3156ee35
SG
257 This function tests if U-Boot rejects an image when a required key isn't
258 used to sign a FIT.
ce5172cf
PR
259
260 Args:
3156ee35 261 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
eb7690e8
PR
262 padding: Either '' or '-pss', to select the padding to use for the
263 rsa signature algorithm.
264 sign_options: Options to mkimage when signing a fit image.
ce5172cf
PR
265 """
266 # Compile our device tree files for kernel and U-Boot. These are
267 # regenerated here since mkimage will modify them (by adding a
268 # public key) below.
269 dtc('sandbox-kernel.dts')
270 dtc('sandbox-u-boot.dts')
271
ce5172cf 272 cons.log.action('%s: Test FIT with configs images' % sha_algo)
3156ee35
SG
273
274 # Build the FIT with prod key (keys required) and sign it. This puts the
275 # signature into sandbox-u-boot.dtb, marked 'required'
b008677d 276 make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding))
eb7690e8 277 sign_fit(sha_algo, sign_options)
3156ee35
SG
278
279 # Build the FIT with dev key (keys NOT required). This adds the
280 # signature into sandbox-u-boot.dtb, NOT marked 'required'.
b008677d 281 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
eb7690e8 282 sign_fit(sha_algo, sign_options)
ce5172cf 283
3156ee35
SG
284 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
285 # Only the prod key is set as 'required'. But FIT we just built has
286 # a dev signature only (sign_fit() overwrites the FIT).
287 # Try to boot the FIT with dev key. This FIT should not be accepted by
288 # U-Boot because the prod key is required.
289 run_bootm(sha_algo, 'required key', '', False)
ce5172cf 290
8729d582
SG
291 cons = u_boot_console
292 tmpdir = cons.config.result_dir + '/'
c9ba60c4 293 datadir = cons.config.source_dir + '/test/py/tests/vboot/'
8729d582
SG
294 fit = '%stest.fit' % tmpdir
295 mkimage = cons.config.build_dir + '/tools/mkimage'
296 fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
297 dtc_args = '-I dts -O dtb -i %s' % tmpdir
298 dtb = '%ssandbox-u-boot.dtb' % tmpdir
ed47097a 299 sig_node = '/configurations/conf-1/signature'
8729d582 300
da76ed27
SG
301 create_rsa_pair('dev')
302 create_rsa_pair('prod')
ce5172cf 303
8729d582
SG
304 # Create a number kernel image with zeroes
305 with open('%stest-kernel.bin' % tmpdir, 'w') as fd:
0e29648f 306 fd.write(500 * chr(0))
8729d582
SG
307
308 try:
309 # We need to use our own device tree file. Remember to restore it
310 # afterwards.
311 old_dtb = cons.config.dtb
312 cons.config.dtb = dtb
1b090032 313 if required:
eb7690e8 314 test_required_key(sha_algo, padding, sign_options)
1b090032 315 else:
eb7690e8 316 test_with_algo(sha_algo, padding, sign_options)
8729d582 317 finally:
27c087d5 318 # Go back to the original U-Boot with the correct dtb.
8729d582 319 cons.config.dtb = old_dtb
27c087d5 320 cons.restart_uboot()
This page took 0.260923 seconds and 4 git commands to generate.