blob: 040147d88b8fc14396764ffd7606b241d91a373a [file] [log] [blame]
Simon Glassd977ecd2016-07-03 09:40:46 -06001# SPDX-License-Identifier: GPL-2.0+
Tom Rini10e47792018-05-06 17:58:06 -04002# Copyright (c) 2016, Google Inc.
Simon Glassd977ecd2016-07-03 09:40:46 -06003#
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
Simon Glassd5deca02016-07-31 17:35:04 -060018- Check that image verification works
Simon Glassd977ecd2016-07-03 09:40:46 -060019- 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
Philippe Reynes5d472d32022-03-28 22:57:06 +020024For pre-load header verification:
25- Create FIT image with a pre-load header
26- Check that signature verification succeeds
27- Corrupt the FIT image
28- Check that signature verification fails
29- Launch an FIT image without a pre-load header
30- Check that image verification fails
31
Simon Glassd977ecd2016-07-03 09:40:46 -060032Tests run with both SHA1 and SHA256 hashing.
33"""
34
Simon Glasse9eeca82021-09-19 15:14:48 -060035import os
Simon Glass5e942f72021-02-15 17:08:08 -070036import shutil
Teddy Reede6a47832018-06-09 11:38:05 -040037import struct
Simon Glass861b5042020-03-18 11:44:05 -060038import pytest
Simon Glassd977ecd2016-07-03 09:40:46 -060039import u_boot_utils as util
Simon Glassc35df8f2020-03-18 11:43:59 -060040import vboot_forge
Simon Glass5e942f72021-02-15 17:08:08 -070041import vboot_evil
Simon Glassd977ecd2016-07-03 09:40:46 -060042
Simon Glass5e942f72021-02-15 17:08:08 -070043# Only run the full suite on a few combinations, since it doesn't add any more
44# test coverage.
Simon Glassa0ba39d2020-03-18 11:44:00 -060045TESTDATA = [
Philippe Reynes5d472d32022-03-28 22:57:06 +020046 ['sha1-basic', 'sha1', '', None, False, True, False, False],
47 ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False, False],
48 ['sha1-pss', 'sha1', '-pss', None, False, False, False, False],
49 ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False, False],
50 ['sha256-basic', 'sha256', '', None, False, False, False, False],
51 ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False, False],
52 ['sha256-pss', 'sha256', '-pss', None, False, False, False, False],
53 ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False, False],
54 ['sha256-pss-required', 'sha256', '-pss', None, True, False, False, False],
55 ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False, False],
56 ['sha384-basic', 'sha384', '', None, False, False, False, False],
57 ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False, False],
58 ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True, False],
59 ['sha256-global-sign', 'sha256', '', '', False, False, False, True],
60 ['sha256-global-sign-pss', 'sha256', '-pss', '', False, False, False, True],
Simon Glassa0ba39d2020-03-18 11:44:00 -060061]
62
Michal Simek6e035ab2016-07-18 08:49:08 +020063@pytest.mark.boardspec('sandbox')
Simon Glassd977ecd2016-07-03 09:40:46 -060064@pytest.mark.buildconfigspec('fit_signature')
Stephen Warren2079db32017-09-18 11:11:49 -060065@pytest.mark.requiredtool('dtc')
66@pytest.mark.requiredtool('fdtget')
67@pytest.mark.requiredtool('fdtput')
68@pytest.mark.requiredtool('openssl')
Philippe Reynes5d472d32022-03-28 22:57:06 +020069@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg,global_sign",
Simon Glass5e942f72021-02-15 17:08:08 -070070 TESTDATA)
Simon Glasse9eeca82021-09-19 15:14:48 -060071def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
Philippe Reynes5d472d32022-03-28 22:57:06 +020072 full_test, algo_arg, global_sign):
Simon Glassd977ecd2016-07-03 09:40:46 -060073 """Test verified boot signing with mkimage and verification with 'bootm'.
74
75 This works using sandbox only as it needs to update the device tree used
76 by U-Boot to hold public keys from the signing process.
77
78 The SHA1 and SHA256 tests are combined into a single test since the
79 key-generation process is quite slow and we want to avoid doing it twice.
80 """
81 def dtc(dts):
Simon Glassd5deca02016-07-31 17:35:04 -060082 """Run the device tree compiler to compile a .dts file
Simon Glassd977ecd2016-07-03 09:40:46 -060083
84 The output file will be the same as the input file but with a .dtb
85 extension.
86
87 Args:
88 dts: Device tree file to compile.
89 """
90 dtb = dts.replace('.dts', '.dtb')
Simon Glassba8116c2016-07-31 17:35:05 -060091 util.run_and_log(cons, 'dtc %s %s%s -O dtb '
92 '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
Simon Glassd977ecd2016-07-03 09:40:46 -060093
Philippe Reynes5d472d32022-03-28 22:57:06 +020094 def dtc_options(dts, options):
95 """Run the device tree compiler to compile a .dts file
96
97 The output file will be the same as the input file but with a .dtb
98 extension.
99
100 Args:
101 dts: Device tree file to compile.
102 options: Options provided to the compiler.
103 """
104 dtb = dts.replace('.dts', '.dtb')
105 util.run_and_log(cons, 'dtc %s %s%s -O dtb '
106 '-o %s%s %s' % (dtc_args, datadir, dts, tmpdir, dtb, options))
107
108 def run_binman(dtb):
109 """Run binman to build an image
110
111 Args:
112 dtb: Device tree file used as input file.
113 """
114 pythonpath = os.environ.get('PYTHONPATH', '')
115 os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/../scripts/dtc/pylibfdt' % tmpdir
116 util.run_and_log(cons, [binman, 'build', '-d', "%s/%s" % (tmpdir,dtb),
117 '-a', "pre-load-key-path=%s" % tmpdir, '-O',
118 tmpdir, '-I', tmpdir])
119 os.environ['PYTHONPATH'] = pythonpath
120
Simon Glass5e942f72021-02-15 17:08:08 -0700121 def run_bootm(sha_algo, test_type, expect_string, boots, fit=None):
Simon Glassd977ecd2016-07-03 09:40:46 -0600122 """Run a 'bootm' command U-Boot.
123
124 This always starts a fresh U-Boot instance since the device tree may
125 contain a new public key.
126
127 Args:
Simon Glassf223c732016-07-31 17:35:06 -0600128 test_type: A string identifying the test type.
129 expect_string: A string which is expected in the output.
130 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
131 use.
Tom Rinib65ce462016-09-18 09:46:58 -0400132 boots: A boolean that is True if Linux should boot and False if
133 we are expected to not boot
Simon Glass5e942f72021-02-15 17:08:08 -0700134 fit: FIT filename to load and verify
Simon Glassd977ecd2016-07-03 09:40:46 -0600135 """
Simon Glass5e942f72021-02-15 17:08:08 -0700136 if not fit:
137 fit = '%stest.fit' % tmpdir
Simon Glass37c2ce12016-07-31 17:35:08 -0600138 cons.restart_uboot()
Simon Glass2a40d832016-07-31 17:35:07 -0600139 with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)):
140 output = cons.run_command_list(
Simon Glass5e942f72021-02-15 17:08:08 -0700141 ['host load hostfs - 100 %s' % fit,
Simon Glass861b5042020-03-18 11:44:05 -0600142 'fdt addr 100',
143 'bootm 100'])
144 assert expect_string in ''.join(output)
Tom Rinib65ce462016-09-18 09:46:58 -0400145 if boots:
Simon Glass861b5042020-03-18 11:44:05 -0600146 assert 'sandbox: continuing, as we cannot run' in ''.join(output)
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200147 else:
Simon Glass724c03b2020-03-18 11:44:04 -0600148 assert('sandbox: continuing, as we cannot run'
149 not in ''.join(output))
Simon Glassd977ecd2016-07-03 09:40:46 -0600150
151 def make_fit(its):
Simon Glassd5deca02016-07-31 17:35:04 -0600152 """Make a new FIT from the .its source file.
Simon Glassd977ecd2016-07-03 09:40:46 -0600153
154 This runs 'mkimage -f' to create a new FIT.
155
156 Args:
Simon Glassd5deca02016-07-31 17:35:04 -0600157 its: Filename containing .its source.
Simon Glassd977ecd2016-07-03 09:40:46 -0600158 """
159 util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f',
160 '%s%s' % (datadir, its), fit])
161
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200162 def sign_fit(sha_algo, options):
Simon Glassd977ecd2016-07-03 09:40:46 -0600163 """Sign the FIT
164
165 Signs the FIT and writes the signature into it. It also writes the
166 public key into the dtb.
Simon Glassf223c732016-07-31 17:35:06 -0600167
168 Args:
169 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
170 use.
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200171 options: Options to provide to mkimage.
Simon Glassd977ecd2016-07-03 09:40:46 -0600172 """
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200173 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
174 if options:
175 args += options.split(' ')
Simon Glassf223c732016-07-31 17:35:06 -0600176 cons.log.action('%s: Sign images' % sha_algo)
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200177 util.run_and_log(cons, args)
Simon Glassd977ecd2016-07-03 09:40:46 -0600178
Philippe Reynes5d472d32022-03-28 22:57:06 +0200179 def sign_fit_dtb(sha_algo, options, dtb):
180 """Sign the FIT
181
182 Signs the FIT and writes the signature into it. It also writes the
183 public key into the dtb.
184
185 Args:
186 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
187 use.
188 options: Options to provide to mkimage.
189 """
190 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
191 if options:
192 args += options.split(' ')
193 cons.log.action('%s: Sign images' % sha_algo)
194 util.run_and_log(cons, args)
195
Thirupathaiah Annapureddy7e703f72020-08-16 23:01:10 -0700196 def sign_fit_norequire(sha_algo, options):
197 """Sign the FIT
198
199 Signs the FIT and writes the signature into it. It also writes the
200 public key into the dtb. It does not mark key as 'required' in dtb.
201
202 Args:
203 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
204 use.
205 options: Options to provide to mkimage.
206 """
207 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, fit]
208 if options:
209 args += options.split(' ')
210 cons.log.action('%s: Sign images' % sha_algo)
211 util.run_and_log(cons, args)
212
Teddy Reede6a47832018-06-09 11:38:05 -0400213 def replace_fit_totalsize(size):
214 """Replace FIT header's totalsize with something greater.
215
216 The totalsize must be less than or equal to FIT_SIGNATURE_MAX_SIZE.
217 If the size is greater, the signature verification should return false.
218
219 Args:
220 size: The new totalsize of the header
221
222 Returns:
223 prev_size: The previous totalsize read from the header
224 """
225 total_size = 0
226 with open(fit, 'r+b') as handle:
227 handle.seek(4)
228 total_size = handle.read(4)
229 handle.seek(4)
230 handle.write(struct.pack(">I", size))
231 return struct.unpack(">I", total_size)[0]
232
Philippe Reynes5d472d32022-03-28 22:57:06 +0200233 def corrupt_file(fit, offset, value):
234 """Corrupt a file
235
236 To corrupt a file, a value is written at the specified offset
237
238 Args:
239 fit: The file to corrupt
240 offset: Offset to write
241 value: Value written
242 """
243 with open(fit, 'r+b') as handle:
244 handle.seek(offset)
245 handle.write(struct.pack(">I", value))
246
Simon Glassb4a2f6a2020-03-18 11:44:07 -0600247 def create_rsa_pair(name):
248 """Generate a new RSA key paid and certificate
249
250 Args:
251 name: Name of of the key (e.g. 'dev')
252 """
253 public_exponent = 65537
Jamin Lin5975ad72022-01-19 16:23:21 +0800254
255 if sha_algo == "sha384":
256 rsa_keygen_bits = 3072
257 else:
258 rsa_keygen_bits = 2048
259
Simon Glassb4a2f6a2020-03-18 11:44:07 -0600260 util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key '
Jamin Lin5975ad72022-01-19 16:23:21 +0800261 '-pkeyopt rsa_keygen_bits:%d '
Simon Glassb4a2f6a2020-03-18 11:44:07 -0600262 '-pkeyopt rsa_keygen_pubexp:%d' %
Jamin Lin5975ad72022-01-19 16:23:21 +0800263 (tmpdir, name, rsa_keygen_bits, public_exponent))
Simon Glassb4a2f6a2020-03-18 11:44:07 -0600264
265 # Create a certificate containing the public key
266 util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key '
267 '-out %s%s.crt' % (tmpdir, name, tmpdir, name))
268
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200269 def test_with_algo(sha_algo, padding, sign_options):
Simon Glassd5deca02016-07-31 17:35:04 -0600270 """Test verified boot with the given hash algorithm.
Simon Glassd977ecd2016-07-03 09:40:46 -0600271
272 This is the main part of the test code. The same procedure is followed
273 for both hashing algorithms.
274
275 Args:
Simon Glassf223c732016-07-31 17:35:06 -0600276 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
277 use.
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200278 padding: Either '' or '-pss', to select the padding to use for the
279 rsa signature algorithm.
280 sign_options: Options to mkimage when signing a fit image.
Simon Glassd977ecd2016-07-03 09:40:46 -0600281 """
Simon Glassdc3ab7e2016-07-31 17:35:02 -0600282 # Compile our device tree files for kernel and U-Boot. These are
283 # regenerated here since mkimage will modify them (by adding a
284 # public key) below.
Simon Glassd977ecd2016-07-03 09:40:46 -0600285 dtc('sandbox-kernel.dts')
286 dtc('sandbox-u-boot.dts')
287
288 # Build the FIT, but don't sign anything yet
Simon Glassf223c732016-07-31 17:35:06 -0600289 cons.log.action('%s: Test FIT with signed images' % sha_algo)
Simon Glass861b5042020-03-18 11:44:05 -0600290 make_fit('sign-images-%s%s.its' % (sha_algo, padding))
Jan Kiszka30f64652022-02-03 21:43:50 +0100291 run_bootm(sha_algo, 'unsigned images', ' - OK' if algo_arg else 'dev-', True)
Simon Glassd977ecd2016-07-03 09:40:46 -0600292
293 # Sign images with our dev keys
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200294 sign_fit(sha_algo, sign_options)
Tom Rinib65ce462016-09-18 09:46:58 -0400295 run_bootm(sha_algo, 'signed images', 'dev+', True)
Simon Glassd977ecd2016-07-03 09:40:46 -0600296
297 # Create a fresh .dtb without the public keys
298 dtc('sandbox-u-boot.dts')
299
Simon Glassf223c732016-07-31 17:35:06 -0600300 cons.log.action('%s: Test FIT with signed configuration' % sha_algo)
Simon Glass861b5042020-03-18 11:44:05 -0600301 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
Jan Kiszka30f64652022-02-03 21:43:50 +0100302 run_bootm(sha_algo, 'unsigned config', '%s+ OK' % ('sha256' if algo_arg else sha_algo), True)
Simon Glassd977ecd2016-07-03 09:40:46 -0600303
304 # Sign images with our dev keys
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200305 sign_fit(sha_algo, sign_options)
Tom Rinib65ce462016-09-18 09:46:58 -0400306 run_bootm(sha_algo, 'signed config', 'dev+', True)
Simon Glassd977ecd2016-07-03 09:40:46 -0600307
Simon Glassf223c732016-07-31 17:35:06 -0600308 cons.log.action('%s: Check signed config on the host' % sha_algo)
Simon Glassd977ecd2016-07-03 09:40:46 -0600309
Simon Glassf411a892020-03-18 11:43:58 -0600310 util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
Simon Glassd977ecd2016-07-03 09:40:46 -0600311
Simon Glass5e942f72021-02-15 17:08:08 -0700312 if full_test:
Simon Glassb823daa2021-02-15 17:08:12 -0700313 # Make sure that U-Boot checks that the config is in the list of
314 # hashed nodes. If it isn't, a security bypass is possible.
Simon Glass5e942f72021-02-15 17:08:08 -0700315 ffit = '%stest.forged.fit' % tmpdir
316 shutil.copyfile(fit, ffit)
317 with open(ffit, 'rb') as fd:
318 root, strblock = vboot_forge.read_fdt(fd)
319 root, strblock = vboot_forge.manipulate(root, strblock)
320 with open(ffit, 'w+b') as fd:
321 vboot_forge.write_fdt(root, strblock, fd)
322 util.run_and_log_expect_exception(
323 cons, [fit_check_sign, '-f', ffit, '-k', dtb],
324 1, 'Failed to verify required signature')
325
326 run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit)
Simon Glassc35df8f2020-03-18 11:43:59 -0600327
Simon Glass5e942f72021-02-15 17:08:08 -0700328 # Try adding an evil root node. This should be detected.
329 efit = '%stest.evilf.fit' % tmpdir
330 shutil.copyfile(fit, efit)
331 vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot')
332
333 util.run_and_log_expect_exception(
334 cons, [fit_check_sign, '-f', efit, '-k', dtb],
335 1, 'Failed to verify required signature')
Simon Glass19d2c022021-02-15 17:08:11 -0700336 run_bootm(sha_algo, 'evil fakeroot', 'Bad FIT kernel image format',
337 False, efit)
Simon Glass5e942f72021-02-15 17:08:08 -0700338
339 # Try adding an @ to the kernel node name. This should be detected.
340 efit = '%stest.evilk.fit' % tmpdir
341 shutil.copyfile(fit, efit)
342 vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
343
Simon Glassb823daa2021-02-15 17:08:12 -0700344 msg = 'Signature checking prevents use of unit addresses (@) in nodes'
Simon Glass5e942f72021-02-15 17:08:08 -0700345 util.run_and_log_expect_exception(
346 cons, [fit_check_sign, '-f', efit, '-k', dtb],
Simon Glassb823daa2021-02-15 17:08:12 -0700347 1, msg)
348 run_bootm(sha_algo, 'evil kernel@', msg, False, efit)
Simon Glassc35df8f2020-03-18 11:43:59 -0600349
350 # Create a new properly signed fit and replace header bytes
351 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200352 sign_fit(sha_algo, sign_options)
Teddy Reede6a47832018-06-09 11:38:05 -0400353 bcfg = u_boot_console.config.buildconfig
354 max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0)
355 existing_size = replace_fit_totalsize(max_size + 1)
Simon Glass724c03b2020-03-18 11:44:04 -0600356 run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
357 False)
Teddy Reede6a47832018-06-09 11:38:05 -0400358 cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo)
359
360 # Replace with existing header bytes
361 replace_fit_totalsize(existing_size)
362 run_bootm(sha_algo, 'signed config', 'dev+', True)
363 cons.log.action('%s: Check default FIT header totalsize' % sha_algo)
364
Simon Glassd977ecd2016-07-03 09:40:46 -0600365 # Increment the first byte of the signature, which should cause failure
Simon Glassba8116c2016-07-31 17:35:05 -0600366 sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' %
367 (fit, sig_node))
Simon Glassd977ecd2016-07-03 09:40:46 -0600368 byte_list = sig.split()
369 byte = int(byte_list[0], 16)
Simon Glassdc3ab7e2016-07-31 17:35:02 -0600370 byte_list[0] = '%x' % (byte + 1)
Simon Glassd977ecd2016-07-03 09:40:46 -0600371 sig = ' '.join(byte_list)
Simon Glassba8116c2016-07-31 17:35:05 -0600372 util.run_and_log(cons, 'fdtput -t bx %s %s value %s' %
373 (fit, sig_node, sig))
Simon Glassd977ecd2016-07-03 09:40:46 -0600374
Simon Glass724c03b2020-03-18 11:44:04 -0600375 run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
376 False)
Simon Glassd977ecd2016-07-03 09:40:46 -0600377
Simon Glassf223c732016-07-31 17:35:06 -0600378 cons.log.action('%s: Check bad config on the host' % sha_algo)
Simon Glass861b5042020-03-18 11:44:05 -0600379 util.run_and_log_expect_exception(
380 cons, [fit_check_sign, '-f', fit, '-k', dtb],
381 1, 'Failed to verify required signature')
Simon Glassd977ecd2016-07-03 09:40:46 -0600382
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200383 def test_required_key(sha_algo, padding, sign_options):
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200384 """Test verified boot with the given hash algorithm.
385
Simon Glass724c03b2020-03-18 11:44:04 -0600386 This function tests if U-Boot rejects an image when a required key isn't
387 used to sign a FIT.
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200388
389 Args:
Simon Glass724c03b2020-03-18 11:44:04 -0600390 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200391 padding: Either '' or '-pss', to select the padding to use for the
392 rsa signature algorithm.
393 sign_options: Options to mkimage when signing a fit image.
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200394 """
395 # Compile our device tree files for kernel and U-Boot. These are
396 # regenerated here since mkimage will modify them (by adding a
397 # public key) below.
398 dtc('sandbox-kernel.dts')
399 dtc('sandbox-u-boot.dts')
400
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200401 cons.log.action('%s: Test FIT with configs images' % sha_algo)
Simon Glass724c03b2020-03-18 11:44:04 -0600402
403 # Build the FIT with prod key (keys required) and sign it. This puts the
404 # signature into sandbox-u-boot.dtb, marked 'required'
Simon Glass861b5042020-03-18 11:44:05 -0600405 make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding))
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200406 sign_fit(sha_algo, sign_options)
Simon Glass724c03b2020-03-18 11:44:04 -0600407
408 # Build the FIT with dev key (keys NOT required). This adds the
409 # signature into sandbox-u-boot.dtb, NOT marked 'required'.
Simon Glass861b5042020-03-18 11:44:05 -0600410 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
Thirupathaiah Annapureddy7e703f72020-08-16 23:01:10 -0700411 sign_fit_norequire(sha_algo, sign_options)
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200412
Simon Glass724c03b2020-03-18 11:44:04 -0600413 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
414 # Only the prod key is set as 'required'. But FIT we just built has
Thirupathaiah Annapureddy7e703f72020-08-16 23:01:10 -0700415 # a dev signature only (sign_fit_norequire() overwrites the FIT).
Simon Glass724c03b2020-03-18 11:44:04 -0600416 # Try to boot the FIT with dev key. This FIT should not be accepted by
417 # U-Boot because the prod key is required.
418 run_bootm(sha_algo, 'required key', '', False)
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200419
Thirupathaiah Annapureddy7e703f72020-08-16 23:01:10 -0700420 # Build the FIT with dev key (keys required) and sign it. This puts the
421 # signature into sandbox-u-boot.dtb, marked 'required'.
422 make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
423 sign_fit(sha_algo, sign_options)
424
425 # Set the required-mode policy to "any".
426 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
427 # Both the dev and prod key are set as 'required'. But FIT we just built has
428 # a dev signature only (sign_fit() overwrites the FIT).
429 # Try to boot the FIT with dev key. This FIT should be accepted by
430 # U-Boot because the dev key is required and policy is "any" required key.
431 util.run_and_log(cons, 'fdtput -t s %s /signature required-mode any' %
432 (dtb))
433 run_bootm(sha_algo, 'multi required key', 'dev+', True)
434
435 # Set the required-mode policy to "all".
436 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
437 # Both the dev and prod key are set as 'required'. But FIT we just built has
438 # a dev signature only (sign_fit() overwrites the FIT).
439 # Try to boot the FIT with dev key. This FIT should not be accepted by
440 # U-Boot because the prod key is required and policy is "all" required key
441 util.run_and_log(cons, 'fdtput -t s %s /signature required-mode all' %
442 (dtb))
443 run_bootm(sha_algo, 'multi required key', '', False)
444
Philippe Reynes5d472d32022-03-28 22:57:06 +0200445 def test_global_sign(sha_algo, padding, sign_options):
446 """Test global image signature with the given hash algorithm and padding.
447
448 Args:
449 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
450 padding: Either '' or '-pss', to select the padding to use for the
451 rsa signature algorithm.
452 """
453
454 dtb = '%ssandbox-u-boot-global%s.dtb' % (tmpdir, padding)
455 cons.config.dtb = dtb
456
457 # Compile our device tree files for kernel and U-Boot. These are
458 # regenerated here since mkimage will modify them (by adding a
459 # public key) below.
460 dtc('sandbox-kernel.dts')
461 dtc_options('sandbox-u-boot-global%s.dts' % padding, '-p 1024')
462
463 # Build the FIT with dev key (keys NOT required). This adds the
464 # signature into sandbox-u-boot.dtb, NOT marked 'required'.
465 make_fit('simple-images.its')
466 sign_fit_dtb(sha_algo, '', dtb)
467
468 # Build the dtb for binman that define the pre-load header
469 # with the global sigature.
470 dtc('sandbox-binman%s.dts' % padding)
471
472 # Run binman to create the final image with the not signed fit
473 # and the pre-load header that contains the global signature.
474 run_binman('sandbox-binman%s.dtb' % padding)
475
476 # Check that the signature is correctly verified by u-boot
477 run_bootm(sha_algo, 'global image signature',
478 'signature check has succeed', True, "%ssandbox.img" % tmpdir)
479
480 # Corrupt the image (just one byte after the pre-load header)
481 corrupt_file("%ssandbox.img" % tmpdir, 4096, 255);
482
483 # Check that the signature verification fails
484 run_bootm(sha_algo, 'global image signature',
485 'signature check has failed', False, "%ssandbox.img" % tmpdir)
486
487 # Check that the boot fails if the global signature is not provided
488 run_bootm(sha_algo, 'global image signature', 'signature is mandatory', False)
489
Simon Glassd977ecd2016-07-03 09:40:46 -0600490 cons = u_boot_console
Simon Glasse9eeca82021-09-19 15:14:48 -0600491 tmpdir = os.path.join(cons.config.result_dir, name) + '/'
492 if not os.path.exists(tmpdir):
493 os.mkdir(tmpdir)
Stephen Warren7047d952016-07-18 10:07:25 -0600494 datadir = cons.config.source_dir + '/test/py/tests/vboot/'
Simon Glassd977ecd2016-07-03 09:40:46 -0600495 fit = '%stest.fit' % tmpdir
496 mkimage = cons.config.build_dir + '/tools/mkimage'
Philippe Reynes5d472d32022-03-28 22:57:06 +0200497 binman = cons.config.source_dir + '/tools/binman/binman'
Simon Glassd977ecd2016-07-03 09:40:46 -0600498 fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
499 dtc_args = '-I dts -O dtb -i %s' % tmpdir
500 dtb = '%ssandbox-u-boot.dtb' % tmpdir
Philippe Reynesa28e9222018-11-14 13:51:05 +0100501 sig_node = '/configurations/conf-1/signature'
Simon Glassd977ecd2016-07-03 09:40:46 -0600502
Simon Glassb4a2f6a2020-03-18 11:44:07 -0600503 create_rsa_pair('dev')
504 create_rsa_pair('prod')
Philippe Reynes1d5ef522019-09-18 16:04:53 +0200505
Simon Glassd977ecd2016-07-03 09:40:46 -0600506 # Create a number kernel image with zeroes
Simon Glass5e942f72021-02-15 17:08:08 -0700507 with open('%stest-kernel.bin' % tmpdir, 'wb') as fd:
508 fd.write(500 * b'\0')
509
510 # Create a second kernel image with ones
511 evil_kernel = '%stest-kernel1.bin' % tmpdir
512 with open(evil_kernel, 'wb') as fd:
513 fd.write(500 * b'\x01')
Simon Glassd977ecd2016-07-03 09:40:46 -0600514
515 try:
516 # We need to use our own device tree file. Remember to restore it
517 # afterwards.
518 old_dtb = cons.config.dtb
519 cons.config.dtb = dtb
Philippe Reynes5d472d32022-03-28 22:57:06 +0200520 if global_sign:
521 test_global_sign(sha_algo, padding, sign_options)
522 elif required:
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200523 test_required_key(sha_algo, padding, sign_options)
Simon Glassa0ba39d2020-03-18 11:44:00 -0600524 else:
Philippe Reynes2fbd17c2020-04-29 15:26:16 +0200525 test_with_algo(sha_algo, padding, sign_options)
Simon Glassd977ecd2016-07-03 09:40:46 -0600526 finally:
Simon Glass37c2ce12016-07-31 17:35:08 -0600527 # Go back to the original U-Boot with the correct dtb.
Simon Glassd977ecd2016-07-03 09:40:46 -0600528 cons.config.dtb = old_dtb
Simon Glass37c2ce12016-07-31 17:35:08 -0600529 cons.restart_uboot()