1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2016, Google Inc. 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 18- Check that image verification works 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 24For 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 32Tests run with both SHA1 and SHA256 hashing. 33 34This also tests fdt_add_pubkey utility in the simple way: 35- Create DTB and FIT files 36- Add keys with fdt_add_pubkey to DTB 37- Sign FIT image 38- Check with fit_check_sign that keys properly added to DTB file 39""" 40 41import os 42import shutil 43import struct 44import pytest 45import u_boot_utils as util 46import vboot_forge 47import vboot_evil 48 49# Common helper functions 50def dtc(dts, cons, dtc_args, datadir, tmpdir, dtb): 51 """Run the device tree compiler to compile a .dts file 52 53 The output file will be the same as the input file but with a .dtb 54 extension. 55 56 Args: 57 dts: Device tree file to compile. 58 cons: U-Boot console. 59 dtc_args: DTC arguments. 60 datadir: Path to data directory. 61 tmpdir: Path to temp directory. 62 dtb: Resulting DTB file. 63 """ 64 dtb = dts.replace('.dts', '.dtb') 65 util.run_and_log(cons, 'dtc %s %s%s -O dtb ' 66 '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb)) 67 68def make_fit(its, cons, mkimage, dtc_args, datadir, fit): 69 """Make a new FIT from the .its source file. 70 71 This runs 'mkimage -f' to create a new FIT. 72 73 Args: 74 its: Filename containing .its source. 75 cons: U-Boot console. 76 mkimage: Path to mkimage utility. 77 dtc_args: DTC arguments. 78 datadir: Path to data directory. 79 fit: Resulting FIT file. 80 """ 81 util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', 82 '%s%s' % (datadir, its), fit]) 83 84# Only run the full suite on a few combinations, since it doesn't add any more 85# test coverage. 86TESTDATA_IN = [ 87 ['sha1-basic', 'sha1', '', None, False, True, False, False], 88 ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False, False], 89 ['sha1-pss', 'sha1', '-pss', None, False, False, False, False], 90 ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False, False], 91 ['sha256-basic', 'sha256', '', None, False, False, False, False], 92 ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False, False], 93 ['sha256-pss', 'sha256', '-pss', None, False, False, False, False], 94 ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False, False], 95 ['sha256-pss-required', 'sha256', '-pss', None, True, False, False, False], 96 ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False, False], 97 ['sha384-basic', 'sha384', '', None, False, False, False, False], 98 ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False, False], 99 ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True, False], 100 ['sha256-global-sign', 'sha256', '', '', False, False, False, True], 101 ['sha256-global-sign-pss', 'sha256', '-pss', '', False, False, False, True], 102] 103 104# Mark all but the first test as slow, so they are not run with '-k not slow' 105TESTDATA = [TESTDATA_IN[0]] 106TESTDATA += [pytest.param(*v, marks=pytest.mark.slow) for v in TESTDATA_IN[1:]] 107 108@pytest.mark.boardspec('sandbox') 109@pytest.mark.buildconfigspec('fit_signature') 110@pytest.mark.requiredtool('dtc') 111@pytest.mark.requiredtool('fdtget') 112@pytest.mark.requiredtool('fdtput') 113@pytest.mark.requiredtool('openssl') 114@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg,global_sign", 115 TESTDATA) 116def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, 117 full_test, algo_arg, global_sign): 118 """Test verified boot signing with mkimage and verification with 'bootm'. 119 120 This works using sandbox only as it needs to update the device tree used 121 by U-Boot to hold public keys from the signing process. 122 123 The SHA1 and SHA256 tests are combined into a single test since the 124 key-generation process is quite slow and we want to avoid doing it twice. 125 """ 126 def dtc_options(dts, options): 127 """Run the device tree compiler to compile a .dts file 128 129 The output file will be the same as the input file but with a .dtb 130 extension. 131 132 Args: 133 dts: Device tree file to compile. 134 options: Options provided to the compiler. 135 """ 136 dtb = dts.replace('.dts', '.dtb') 137 util.run_and_log(cons, 'dtc %s %s%s -O dtb ' 138 '-o %s%s %s' % (dtc_args, datadir, dts, tmpdir, dtb, options)) 139 140 def run_binman(dtb): 141 """Run binman to build an image 142 143 Args: 144 dtb: Device tree file used as input file. 145 """ 146 pythonpath = os.environ.get('PYTHONPATH', '') 147 os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/../scripts/dtc/pylibfdt' % tmpdir 148 util.run_and_log(cons, [binman, 'build', '-d', "%s/%s" % (tmpdir,dtb), 149 '-a', "pre-load-key-path=%s" % tmpdir, '-O', 150 tmpdir, '-I', tmpdir]) 151 os.environ['PYTHONPATH'] = pythonpath 152 153 def run_bootm(sha_algo, test_type, expect_string, boots, fit=None): 154 """Run a 'bootm' command U-Boot. 155 156 This always starts a fresh U-Boot instance since the device tree may 157 contain a new public key. 158 159 Args: 160 test_type: A string identifying the test type. 161 expect_string: A string which is expected in the output. 162 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 163 use. 164 boots: A boolean that is True if Linux should boot and False if 165 we are expected to not boot 166 fit: FIT filename to load and verify 167 """ 168 if not fit: 169 fit = '%stest.fit' % tmpdir 170 cons.restart_uboot() 171 with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)): 172 output = cons.run_command_list( 173 ['host load hostfs - 100 %s' % fit, 174 'fdt addr 100', 175 'bootm 100']) 176 assert expect_string in ''.join(output) 177 if boots: 178 assert 'sandbox: continuing, as we cannot run' in ''.join(output) 179 else: 180 assert('sandbox: continuing, as we cannot run' 181 not in ''.join(output)) 182 183 def sign_fit(sha_algo, options): 184 """Sign the FIT 185 186 Signs the FIT and writes the signature into it. It also writes the 187 public key into the dtb. 188 189 Args: 190 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 191 use. 192 options: Options to provide to mkimage. 193 """ 194 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit] 195 if options: 196 args += options.split(' ') 197 cons.log.action('%s: Sign images' % sha_algo) 198 util.run_and_log(cons, args) 199 200 def sign_fit_dtb(sha_algo, options, dtb): 201 """Sign the FIT 202 203 Signs the FIT and writes the signature into it. It also writes the 204 public key into the dtb. 205 206 Args: 207 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 208 use. 209 options: Options to provide to mkimage. 210 """ 211 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit] 212 if options: 213 args += options.split(' ') 214 cons.log.action('%s: Sign images' % sha_algo) 215 util.run_and_log(cons, args) 216 217 def sign_fit_norequire(sha_algo, options): 218 """Sign the FIT 219 220 Signs the FIT and writes the signature into it. It also writes the 221 public key into the dtb. It does not mark key as 'required' in dtb. 222 223 Args: 224 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 225 use. 226 options: Options to provide to mkimage. 227 """ 228 args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, fit] 229 if options: 230 args += options.split(' ') 231 cons.log.action('%s: Sign images' % sha_algo) 232 util.run_and_log(cons, args) 233 234 def replace_fit_totalsize(size): 235 """Replace FIT header's totalsize with something greater. 236 237 The totalsize must be less than or equal to FIT_SIGNATURE_MAX_SIZE. 238 If the size is greater, the signature verification should return false. 239 240 Args: 241 size: The new totalsize of the header 242 243 Returns: 244 prev_size: The previous totalsize read from the header 245 """ 246 total_size = 0 247 with open(fit, 'r+b') as handle: 248 handle.seek(4) 249 total_size = handle.read(4) 250 handle.seek(4) 251 handle.write(struct.pack(">I", size)) 252 return struct.unpack(">I", total_size)[0] 253 254 def corrupt_file(fit, offset, value): 255 """Corrupt a file 256 257 To corrupt a file, a value is written at the specified offset 258 259 Args: 260 fit: The file to corrupt 261 offset: Offset to write 262 value: Value written 263 """ 264 with open(fit, 'r+b') as handle: 265 handle.seek(offset) 266 handle.write(struct.pack(">I", value)) 267 268 def create_rsa_pair(name): 269 """Generate a new RSA key paid and certificate 270 271 Args: 272 name: Name of of the key (e.g. 'dev') 273 """ 274 public_exponent = 65537 275 276 if sha_algo == "sha384": 277 rsa_keygen_bits = 3072 278 else: 279 rsa_keygen_bits = 2048 280 281 util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key ' 282 '-pkeyopt rsa_keygen_bits:%d ' 283 '-pkeyopt rsa_keygen_pubexp:%d' % 284 (tmpdir, name, rsa_keygen_bits, public_exponent)) 285 286 # Create a certificate containing the public key 287 util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key ' 288 '-out %s%s.crt' % (tmpdir, name, tmpdir, name)) 289 290 def test_with_algo(sha_algo, padding, sign_options): 291 """Test verified boot with the given hash algorithm. 292 293 This is the main part of the test code. The same procedure is followed 294 for both hashing algorithms. 295 296 Args: 297 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 298 use. 299 padding: Either '' or '-pss', to select the padding to use for the 300 rsa signature algorithm. 301 sign_options: Options to mkimage when signing a fit image. 302 """ 303 # Compile our device tree files for kernel and U-Boot. These are 304 # regenerated here since mkimage will modify them (by adding a 305 # public key) below. 306 dtc('sandbox-kernel.dts', cons, dtc_args, datadir, tmpdir, dtb) 307 dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb) 308 309 # Build the FIT, but don't sign anything yet 310 cons.log.action('%s: Test FIT with signed images' % sha_algo) 311 make_fit('sign-images-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 312 run_bootm(sha_algo, 'unsigned images', ' - OK' if algo_arg else 'dev-', True) 313 314 # Sign images with our dev keys 315 sign_fit(sha_algo, sign_options) 316 run_bootm(sha_algo, 'signed images', 'dev+', True) 317 318 # Create a fresh .dtb without the public keys 319 dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb) 320 321 cons.log.action('%s: Test FIT with signed configuration' % sha_algo) 322 make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 323 run_bootm(sha_algo, 'unsigned config', '%s+ OK' % ('sha256' if algo_arg else sha_algo), True) 324 325 # Sign images with our dev keys 326 sign_fit(sha_algo, sign_options) 327 run_bootm(sha_algo, 'signed config', 'dev+', True) 328 329 cons.log.action('%s: Check signed config on the host' % sha_algo) 330 331 util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb]) 332 333 if full_test: 334 # Make sure that U-Boot checks that the config is in the list of 335 # hashed nodes. If it isn't, a security bypass is possible. 336 ffit = '%stest.forged.fit' % tmpdir 337 shutil.copyfile(fit, ffit) 338 with open(ffit, 'rb') as fd: 339 root, strblock = vboot_forge.read_fdt(fd) 340 root, strblock = vboot_forge.manipulate(root, strblock) 341 with open(ffit, 'w+b') as fd: 342 vboot_forge.write_fdt(root, strblock, fd) 343 util.run_and_log_expect_exception( 344 cons, [fit_check_sign, '-f', ffit, '-k', dtb], 345 1, 'Failed to verify required signature') 346 347 run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit) 348 349 # Try adding an evil root node. This should be detected. 350 efit = '%stest.evilf.fit' % tmpdir 351 shutil.copyfile(fit, efit) 352 vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot') 353 354 util.run_and_log_expect_exception( 355 cons, [fit_check_sign, '-f', efit, '-k', dtb], 356 1, 'Failed to verify required signature') 357 run_bootm(sha_algo, 'evil fakeroot', 'Bad FIT kernel image format', 358 False, efit) 359 360 # Try adding an @ to the kernel node name. This should be detected. 361 efit = '%stest.evilk.fit' % tmpdir 362 shutil.copyfile(fit, efit) 363 vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@') 364 365 msg = 'Signature checking prevents use of unit addresses (@) in nodes' 366 util.run_and_log_expect_exception( 367 cons, [fit_check_sign, '-f', efit, '-k', dtb], 368 1, msg) 369 run_bootm(sha_algo, 'evil kernel@', msg, False, efit) 370 371 # Create a new properly signed fit and replace header bytes 372 make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 373 sign_fit(sha_algo, sign_options) 374 bcfg = u_boot_console.config.buildconfig 375 max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0) 376 existing_size = replace_fit_totalsize(max_size + 1) 377 run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', 378 False) 379 cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo) 380 381 # Replace with existing header bytes 382 replace_fit_totalsize(existing_size) 383 run_bootm(sha_algo, 'signed config', 'dev+', True) 384 cons.log.action('%s: Check default FIT header totalsize' % sha_algo) 385 386 # Increment the first byte of the signature, which should cause failure 387 sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' % 388 (fit, sig_node)) 389 byte_list = sig.split() 390 byte = int(byte_list[0], 16) 391 byte_list[0] = '%x' % (byte + 1) 392 sig = ' '.join(byte_list) 393 util.run_and_log(cons, 'fdtput -t bx %s %s value %s' % 394 (fit, sig_node, sig)) 395 396 run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', 397 False) 398 399 cons.log.action('%s: Check bad config on the host' % sha_algo) 400 util.run_and_log_expect_exception( 401 cons, [fit_check_sign, '-f', fit, '-k', dtb], 402 1, 'Failed to verify required signature') 403 404 def test_required_key(sha_algo, padding, sign_options): 405 """Test verified boot with the given hash algorithm. 406 407 This function tests if U-Boot rejects an image when a required key isn't 408 used to sign a FIT. 409 410 Args: 411 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use 412 padding: Either '' or '-pss', to select the padding to use for the 413 rsa signature algorithm. 414 sign_options: Options to mkimage when signing a fit image. 415 """ 416 # Compile our device tree files for kernel and U-Boot. These are 417 # regenerated here since mkimage will modify them (by adding a 418 # public key) below. 419 dtc('sandbox-kernel.dts', cons, dtc_args, datadir, tmpdir, dtb) 420 dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb) 421 422 cons.log.action('%s: Test FIT with configs images' % sha_algo) 423 424 # Build the FIT with prod key (keys required) and sign it. This puts the 425 # signature into sandbox-u-boot.dtb, marked 'required' 426 make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 427 sign_fit(sha_algo, sign_options) 428 429 # Build the FIT with dev key (keys NOT required). This adds the 430 # signature into sandbox-u-boot.dtb, NOT marked 'required'. 431 make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 432 sign_fit_norequire(sha_algo, sign_options) 433 434 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. 435 # Only the prod key is set as 'required'. But FIT we just built has 436 # a dev signature only (sign_fit_norequire() overwrites the FIT). 437 # Try to boot the FIT with dev key. This FIT should not be accepted by 438 # U-Boot because the prod key is required. 439 run_bootm(sha_algo, 'required key', '', False) 440 441 # Build the FIT with dev key (keys required) and sign it. This puts the 442 # signature into sandbox-u-boot.dtb, marked 'required'. 443 make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 444 sign_fit(sha_algo, sign_options) 445 446 # Set the required-mode policy to "any". 447 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. 448 # Both the dev and prod key are set as 'required'. But FIT we just built has 449 # a dev signature only (sign_fit() overwrites the FIT). 450 # Try to boot the FIT with dev key. This FIT should be accepted by 451 # U-Boot because the dev key is required and policy is "any" required key. 452 util.run_and_log(cons, 'fdtput -t s %s /signature required-mode any' % 453 (dtb)) 454 run_bootm(sha_algo, 'multi required key', 'dev+', True) 455 456 # Set the required-mode policy to "all". 457 # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. 458 # Both the dev and prod key are set as 'required'. But FIT we just built has 459 # a dev signature only (sign_fit() overwrites the FIT). 460 # Try to boot the FIT with dev key. This FIT should not be accepted by 461 # U-Boot because the prod key is required and policy is "all" required key 462 util.run_and_log(cons, 'fdtput -t s %s /signature required-mode all' % 463 (dtb)) 464 run_bootm(sha_algo, 'multi required key', '', False) 465 466 def test_global_sign(sha_algo, padding, sign_options): 467 """Test global image signature with the given hash algorithm and padding. 468 469 Args: 470 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use 471 padding: Either '' or '-pss', to select the padding to use for the 472 rsa signature algorithm. 473 """ 474 475 dtb = '%ssandbox-u-boot-global%s.dtb' % (tmpdir, padding) 476 cons.config.dtb = dtb 477 478 # Compile our device tree files for kernel and U-Boot. These are 479 # regenerated here since mkimage will modify them (by adding a 480 # public key) below. 481 dtc('sandbox-kernel.dts', cons, dtc_args, datadir, tmpdir, dtb) 482 dtc_options('sandbox-u-boot-global%s.dts' % padding, '-p 1024') 483 484 # Build the FIT with dev key (keys NOT required). This adds the 485 # signature into sandbox-u-boot.dtb, NOT marked 'required'. 486 make_fit('simple-images.its', cons, mkimage, dtc_args, datadir, fit) 487 sign_fit_dtb(sha_algo, '', dtb) 488 489 # Build the dtb for binman that define the pre-load header 490 # with the global sigature. 491 dtc('sandbox-binman%s.dts' % padding, cons, dtc_args, datadir, tmpdir, dtb) 492 493 # Run binman to create the final image with the not signed fit 494 # and the pre-load header that contains the global signature. 495 run_binman('sandbox-binman%s.dtb' % padding) 496 497 # Check that the signature is correctly verified by u-boot 498 run_bootm(sha_algo, 'global image signature', 499 'signature check has succeed', True, "%ssandbox.img" % tmpdir) 500 501 # Corrupt the image (just one byte after the pre-load header) 502 corrupt_file("%ssandbox.img" % tmpdir, 4096, 255); 503 504 # Check that the signature verification fails 505 run_bootm(sha_algo, 'global image signature', 506 'signature check has failed', False, "%ssandbox.img" % tmpdir) 507 508 # Check that the boot fails if the global signature is not provided 509 run_bootm(sha_algo, 'global image signature', 'signature is mandatory', False) 510 511 cons = u_boot_console 512 tmpdir = os.path.join(cons.config.result_dir, name) + '/' 513 if not os.path.exists(tmpdir): 514 os.mkdir(tmpdir) 515 datadir = cons.config.source_dir + '/test/py/tests/vboot/' 516 fit = '%stest.fit' % tmpdir 517 mkimage = cons.config.build_dir + '/tools/mkimage' 518 binman = cons.config.source_dir + '/tools/binman/binman' 519 fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' 520 dtc_args = '-I dts -O dtb -i %s' % tmpdir 521 dtb = '%ssandbox-u-boot.dtb' % tmpdir 522 sig_node = '/configurations/conf-1/signature' 523 524 create_rsa_pair('dev') 525 create_rsa_pair('prod') 526 527 # Create a number kernel image with zeroes 528 with open('%stest-kernel.bin' % tmpdir, 'wb') as fd: 529 fd.write(500 * b'\0') 530 531 # Create a second kernel image with ones 532 evil_kernel = '%stest-kernel1.bin' % tmpdir 533 with open(evil_kernel, 'wb') as fd: 534 fd.write(500 * b'\x01') 535 536 # We need to use our own device tree file. Remember to restore it 537 # afterwards. 538 old_dtb = cons.config.dtb 539 try: 540 cons.config.dtb = dtb 541 if global_sign: 542 test_global_sign(sha_algo, padding, sign_options) 543 elif required: 544 test_required_key(sha_algo, padding, sign_options) 545 else: 546 test_with_algo(sha_algo, padding, sign_options) 547 finally: 548 # Go back to the original U-Boot with the correct dtb. 549 cons.config.dtb = old_dtb 550 cons.restart_uboot() 551 552 553TESTDATA_IN = [ 554 ['sha1-basic', 'sha1', '', None, False], 555 ['sha1-pad', 'sha1', '', '-E -p 0x10000', False], 556 ['sha1-pss', 'sha1', '-pss', None, False], 557 ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False], 558 ['sha256-basic', 'sha256', '', None, False], 559 ['sha256-pad', 'sha256', '', '-E -p 0x10000', False], 560 ['sha256-pss', 'sha256', '-pss', None, False], 561 ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False], 562 ['sha256-pss-required', 'sha256', '-pss', None, False], 563 ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', False], 564 ['sha384-basic', 'sha384', '', None, False], 565 ['sha384-pad', 'sha384', '', '-E -p 0x10000', False], 566 ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', True], 567 ['sha256-global-sign', 'sha256', '', '', False], 568 ['sha256-global-sign-pss', 'sha256', '-pss', '', False], 569] 570 571# Mark all but the first test as slow, so they are not run with '-k not slow' 572TESTDATA = [TESTDATA_IN[0]] 573TESTDATA += [pytest.param(*v, marks=pytest.mark.slow) for v in TESTDATA_IN[1:]] 574 575@pytest.mark.boardspec('sandbox') 576@pytest.mark.buildconfigspec('fit_signature') 577@pytest.mark.requiredtool('dtc') 578@pytest.mark.requiredtool('openssl') 579@pytest.mark.parametrize("name,sha_algo,padding,sign_options,algo_arg", TESTDATA) 580def test_fdt_add_pubkey(u_boot_console, name, sha_algo, padding, sign_options, algo_arg): 581 """Test fdt_add_pubkey utility with bunch of different algo options.""" 582 583 def sign_fit(sha_algo, options): 584 """Sign the FIT 585 586 Signs the FIT and writes the signature into it. 587 588 Args: 589 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to 590 use. 591 options: Options to provide to mkimage. 592 """ 593 args = [mkimage, '-F', '-k', tmpdir, fit] 594 if options: 595 args += options.split(' ') 596 cons.log.action('%s: Sign images' % sha_algo) 597 util.run_and_log(cons, args) 598 599 def test_add_pubkey(sha_algo, padding, sign_options): 600 """Test fdt_add_pubkey utility with given hash algorithm and padding. 601 602 This function tests if fdt_add_pubkey utility may add public keys into dtb. 603 604 Args: 605 sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use 606 padding: Either '' or '-pss', to select the padding to use for the 607 rsa signature algorithm. 608 sign_options: Options to mkimage when signing a fit image. 609 """ 610 611 # Create a fresh .dtb without the public keys 612 dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb) 613 614 cons.log.action('%s: Test fdt_add_pubkey with signed configuration' % sha_algo) 615 # Then add the dev key via the fdt_add_pubkey tool 616 util.run_and_log(cons, [fdt_add_pubkey, '-a', '%s,%s' % ('sha256' if algo_arg else sha_algo, \ 617 'rsa3072' if sha_algo == 'sha384' else 'rsa2048'), 618 '-k', tmpdir, '-n', 'dev', '-r', 'conf', dtb]) 619 620 make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit) 621 622 # Sign images with our dev keys 623 sign_fit(sha_algo, sign_options) 624 625 # Check with fit_check_sign that FIT is signed with key 626 util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb]) 627 628 cons = u_boot_console 629 tmpdir = os.path.join(cons.config.result_dir, name) + '/' 630 if not os.path.exists(tmpdir): 631 os.mkdir(tmpdir) 632 datadir = cons.config.source_dir + '/test/py/tests/vboot/' 633 fit = '%stest.fit' % tmpdir 634 mkimage = cons.config.build_dir + '/tools/mkimage' 635 binman = cons.config.source_dir + '/tools/binman/binman' 636 fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' 637 fdt_add_pubkey = cons.config.build_dir + '/tools/fdt_add_pubkey' 638 dtc_args = '-I dts -O dtb -i %s' % tmpdir 639 dtb = '%ssandbox-u-boot.dtb' % tmpdir 640 641 # keys created in test_vboot test 642 643 test_add_pubkey(sha_algo, padding, sign_options) 644