1#!/usr/bin/env python3 2# 3# Copyright (C) 2024 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16"""Generate test data files for libgbl tests""" 17 18import argparse 19import gzip 20import os 21import pathlib 22import random 23import re 24import shutil 25import subprocess 26import tempfile 27 28SCRIPT_DIR = pathlib.Path(os.path.dirname(os.path.realpath(__file__))) 29AOSP_ROOT = SCRIPT_DIR.parents[4] 30GBL_ROOT = SCRIPT_DIR.parents[1] 31ANDROID_OUT = SCRIPT_DIR / "android" 32GPT_TOOL = GBL_ROOT / "tools" / "gen_gpt_disk.py" 33AVB_DIR = AOSP_ROOT / "external" / "avb" 34AVB_TOOL = AVB_DIR / "avbtool.py" 35MKBOOTIMG_TOOL = AOSP_ROOT / "tools" / "mkbootimg" / "mkbootimg.py" 36UNPACKBOOTIMG_TOOL = AOSP_ROOT / "tools" / "mkbootimg" / "unpack_bootimg.py" 37AVB_TEST_DATA_DIR = AVB_DIR / "test" / "data" 38DTC_TOOL = ( 39 AOSP_ROOT / "prebuilts" / "kernel-build-tools" / "linux-x86" / "bin" / "dtc" 40) 41MKDTBOIMG_TOOL = ( 42 AOSP_ROOT 43 / "prebuilts" 44 / "kernel-build-tools" 45 / "linux-x86" 46 / "bin" 47 / "mkdtboimg" 48) 49LZ4_TOOL = "lz4" 50SZ_KB = 1024 51 52# Manually downloaded from Android CI: 53# https://android-build.corp.google.com/build_explorer/branch/aosp_kernel-common-android-mainline 54GKI_BOOT_GZ = ANDROID_OUT / "gki_boot_gz.img" 55GKI_BOOT_LZ4 = ANDROID_OUT / "gki_boot_lz4.img" 56 57# RNG seed values. Keep the same seed value for a given file to ensure 58# reproducibility as much as possible; this will prevent adding a bunch of 59# unnecessary test binaries to the git history. 60RNG_SEED_SPARSE_TEST_RAW = 1 61RNG_SEED_ZIRCON = {"a": 2, "b": 3, "r": 4, "slotless": 5} 62RNG_SEED_ANDROID = {"a": 6, "b": 7} 63 64# AVB related constants. 65PSK = AVB_TEST_DATA_DIR / "testkey_cert_psk.pem" 66TEST_ROLLBACK_INDEX_LOCATION = 1 67TEST_ROLLBACK_INDEX = 2 68 69 70# A helper for writing bytes to a file at a given offset. 71def write_file(file, offset, data): 72 file.seek(offset, 0) 73 file.write(data) 74 75 76# Unpack kernel from boot image 77def unpack_boot(boot, into): 78 subprocess.run( 79 [ 80 UNPACKBOOTIMG_TOOL, 81 "--boot_img", boot, 82 "--out", into, 83 ], 84 stderr=subprocess.STDOUT, 85 check=True, 86 ) 87 88 89def uncompress_lz4(archive, into): 90 subprocess.run( 91 [ 92 LZ4_TOOL, 93 "-f", # always override 94 "-d", archive, 95 into, 96 ], 97 stderr=subprocess.STDOUT, 98 check=True, 99 ) 100 101 102def uncompress_gz(archive, into): 103 with gzip.open(archive, "rb") as input, open(into, "wb") as output: 104 shutil.copyfileobj(input, output) 105 106 107# Unpack and uncompress GKI boot images 108def unpack_gkis(): 109 with tempfile.TemporaryDirectory() as temp_dir: 110 temp_dir = pathlib.Path(temp_dir) 111 112 if shutil.which(LZ4_TOOL) is not None: 113 unpack_boot(GKI_BOOT_LZ4, temp_dir) 114 shutil.copyfile(temp_dir / "kernel", 115 ANDROID_OUT / "gki_boot_lz4_kernel") 116 uncompress_lz4(ANDROID_OUT / "gki_boot_lz4_kernel", 117 ANDROID_OUT / "gki_boot_lz4_kernel_uncompressed") 118 else: 119 print("Warning: lz4 tool isn't presented, skipping unpack lz4 gki boot") 120 121 unpack_boot(GKI_BOOT_GZ, temp_dir) 122 shutil.copyfile(temp_dir / "kernel", 123 ANDROID_OUT / "gki_boot_gz_kernel") 124 uncompress_gz(ANDROID_OUT / "gki_boot_gz_kernel", 125 ANDROID_OUT / "gki_boot_gz_kernel_uncompressed") 126 127 128# Generates sparse image for flashing test 129def gen_sparse_test_file(): 130 out_file_raw = SCRIPT_DIR / "sparse_test_raw.bin" 131 random.seed(RNG_SEED_SPARSE_TEST_RAW) 132 with open(out_file_raw, "wb") as f: 133 # 4k filled with 0x78563412 134 write_file(f, 0, b"\x12\x34\x56\x78" * 1024) 135 # 8k file hole (will become dont-care with the "-s" option) 136 # 12k raw data 137 write_file(f, 12 * SZ_KB, random.randbytes(12 * SZ_KB)) 138 # 8k filled with 0x78563412 139 write_file(f, 24 * SZ_KB, b"\x12\x34\x56\x78" * 1024 * 2) 140 # 12k raw data 141 write_file(f, 32 * SZ_KB, random.randbytes(12 * SZ_KB)) 142 # 4k filled with 0x78563412 143 write_file(f, 44 * SZ_KB, b"\x12\x34\x56\x78" * 1024) 144 # 8k filled with 0xEFCDAB90 145 write_file(f, 48 * SZ_KB, b"\x90\xab\xcd\xef" * 1024 * 2) 146 147 # For now this requires that img2simg exists on $PATH. 148 # It can be built from an Android checkout via `m img2simg`; the resulting 149 # binary will be at out/host/linux-x86/bin/img2simg. 150 subprocess.run( 151 ["img2simg", "-s", out_file_raw, SCRIPT_DIR / "sparse_test.bin"] 152 ) 153 subprocess.run( 154 [ 155 "img2simg", 156 "-s", 157 out_file_raw, 158 SCRIPT_DIR / "sparse_test_blk1024.bin", 159 "1024", 160 ] 161 ) 162 163 164def gen_dtb(input_dts, output_dtb): 165 subprocess.run( 166 [DTC_TOOL, "-I", "dts", "-O", "dtb", "-o", output_dtb, input_dts], 167 stderr=subprocess.STDOUT, 168 check=True, 169 ) 170 171 172def gen_android_test_dtb(): 173 out_dir = ANDROID_OUT 174 # Generates base test device tree. 175 gen_dtb(out_dir / "device_tree.dts", out_dir / "device_tree.dtb") 176 gen_dtb( 177 out_dir / "device_tree_custom.dts", out_dir / "device_tree_custom.dtb" 178 ) 179 # Generates dtb to be used inside boot/vendor_boot 180 subprocess.run( 181 [ 182 MKDTBOIMG_TOOL, 183 "create", 184 out_dir / "dtb.img", 185 "--id=0x1", 186 "--rev=0x0", 187 out_dir / "device_tree.dtb", 188 ], 189 stderr=subprocess.STDOUT, 190 check=True, 191 ) 192 193 # Generates dtb_a/dtb_b 194 gen_dtb(out_dir / "device_tree_a.dts", out_dir / "device_tree_a.dtb") 195 gen_dtb(out_dir / "device_tree_b.dts", out_dir / "device_tree_b.dtb") 196 subprocess.run( 197 [ 198 MKDTBOIMG_TOOL, 199 "create", 200 out_dir / "dtb_a.img", 201 "--id=0x1", 202 "--rev=0x0", 203 out_dir / "device_tree_a.dtb", 204 ], 205 stderr=subprocess.STDOUT, 206 check=True, 207 ) 208 subprocess.run( 209 [ 210 MKDTBOIMG_TOOL, 211 "create", 212 out_dir / "dtb_b.img", 213 "--id=0x1", 214 "--rev=0x0", 215 out_dir / "device_tree_b.dtb", 216 ], 217 stderr=subprocess.STDOUT, 218 check=True, 219 ) 220 221 # Generates overlay 222 gen_dtb(out_dir / "overlay_a.dts", out_dir / "overlay_a.dtb") 223 gen_dtb(out_dir / "overlay_b.dts", out_dir / "overlay_b.dtb") 224 225 subprocess.run( 226 [ 227 MKDTBOIMG_TOOL, 228 "create", 229 out_dir / "dtbo_a.img", 230 "--id=0x1", 231 "--rev=0x0", 232 out_dir / "overlay_a.dtb", 233 ], 234 stderr=subprocess.STDOUT, 235 check=True, 236 ) 237 subprocess.run( 238 [ 239 MKDTBOIMG_TOOL, 240 "create", 241 out_dir / "dtbo_b.img", 242 "--id=0x1", 243 "--rev=0x0", 244 out_dir / "overlay_b.dtb", 245 ], 246 stderr=subprocess.STDOUT, 247 check=True, 248 ) 249 250 251# Generate vbmeta data for a set of images. 252def gen_android_test_vbmeta(partition_file_pairs, out_vbmeta): 253 with tempfile.TemporaryDirectory() as temp_dir: 254 desc_args = [] 255 temp_dir = pathlib.Path(temp_dir) 256 for i, (part, image_file) in enumerate(partition_file_pairs): 257 out = temp_dir / f"{i}.vbmeta_desc" 258 desc_args += ["--include_descriptors_from_image", out] 259 subprocess.run( 260 [ 261 AVB_TOOL, 262 "add_hash_footer", 263 "--image", 264 image_file, 265 "--partition_name", 266 part, 267 "--do_not_append_vbmeta_image", 268 "--output_vbmeta_image", 269 out, 270 "--salt", 271 "9f06406a750581266f21865d115e63b54db441bc0d614195c78c14451b5ecb8abb14d8cd88d816c4750545ef89cb348a3834815aac4fa359e8b02a740483d975", 272 "--partition_size", 273 "209715200", # Randomly chosen large enough value. 274 ], 275 stderr=subprocess.STDOUT, 276 check=True, 277 ) 278 279 subprocess.run( 280 [ 281 AVB_TOOL, 282 "make_vbmeta_image", 283 "--output", 284 out_vbmeta, 285 "--key", 286 PSK, 287 "--algorithm", 288 "SHA512_RSA4096", 289 "--rollback_index", 290 f"{TEST_ROLLBACK_INDEX}", 291 "--rollback_index_location", 292 f"{TEST_ROLLBACK_INDEX_LOCATION}", 293 ] 294 + desc_args, 295 stderr=subprocess.STDOUT, 296 check=True, 297 ) 298 299 # Generates vbmeta digest file 300 out_digest = out_vbmeta.with_suffix(".digest.txt") 301 digest = subprocess.run( 302 [ 303 AVB_TOOL, 304 "calculate_vbmeta_digest", 305 "--image", 306 out_vbmeta, 307 "--hash_algorithm", 308 "sha512", 309 ], 310 311 312 check=True, 313 text=True, 314 capture_output=True, 315 ) 316 out_digest.write_text(digest.stdout) 317 318 extract_vbmeta_digests(out_vbmeta) 319 320 321# Extract digests from vbmeta data 322def extract_vbmeta_digests(vbmeta): 323 # Get vbmeta digests 324 digests = ( 325 re.split( 326 "\n|: ", 327 subprocess.run( 328 [ 329 AVB_TOOL, 330 "print_partition_digests", 331 "--image", 332 vbmeta, 333 ], 334 335 336 check=True, 337 text=True, 338 capture_output=True, 339 ) 340 .stdout 341 ) 342 ) 343 digests = {digests[i]: digests[i+1] for i in range(0, len(digests), 2) if digests[i] in [ 344 "boot", "vendor_boot", "init_boot", "dtbo", "dtb"]} 345 346 for key, value in digests.items(): 347 out_digest = vbmeta.with_suffix(".{}.digest.txt".format(key)) 348 out_digest.write_text(value + "\n") 349 350 351def gen_android_test_images(): 352 unpack_gkis() 353 gen_android_test_dtb() 354 355 with tempfile.TemporaryDirectory() as temp_dir: 356 temp_dir = pathlib.Path(temp_dir) 357 out_dir = ANDROID_OUT 358 out_dir.mkdir(parents=True, exist_ok=True) 359 for slot in ["a", "b"]: 360 random.seed(RNG_SEED_ANDROID[slot]) 361 kernel = out_dir / f"kernel_{slot}.img" 362 kernel.write_bytes(random.randbytes(4 * SZ_KB)) 363 364 generic_ramdisk = out_dir / f"generic_ramdisk_{slot}.img" 365 generic_ramdisk.write_bytes(random.randbytes(8 * SZ_KB)) 366 367 vendor_ramdisk = out_dir / f"vendor_ramdisk_{slot}.img" 368 vendor_ramdisk.write_bytes(random.randbytes(12 * SZ_KB)) 369 370 vendor_bootconfig = temp_dir / f"vendor_bootconfig_{slot}.img" 371 vendor_bootconfig.write_bytes( 372 b"""\ 373androidboot.config_1=val_1 374androidboot.config_2=val_2 375""" 376 ) 377 378 boot_cmdline = "cmd_key_1=cmd_val_1,cmd_key_2=cmd_val_2" 379 vendor_cmdline = "cmd_vendor_key_1=cmd_vendor_val_1,cmd_vendor_key_2=cmd_vendor_val_2" 380 381 # Generate v3, v4 boot image without ramdisk (usecase for init_boot) 382 common = [ 383 MKBOOTIMG_TOOL, 384 "--kernel", 385 kernel, 386 "--cmdline", 387 boot_cmdline, 388 "--dtb", 389 out_dir / "device_tree.dtb", 390 ] 391 for i in [3, 4]: 392 out = out_dir / f"boot_no_ramdisk_v{i}_{slot}.img" 393 subprocess.run( 394 common + ["--header_version", f"{i}", "-o", out], 395 check=True, 396 stderr=subprocess.STDOUT, 397 ) 398 399 # Generates v0 - v4 boot image that contains generic ramdisk. 400 common += [ 401 "--ramdisk", 402 generic_ramdisk, 403 ] 404 for i in range(0, 5): 405 out = out_dir / f"boot_v{i}_{slot}.img" 406 subprocess.run( 407 common + ["--header_version", f"{i}", "-o", out], 408 check=True, 409 stderr=subprocess.STDOUT, 410 ) 411 412 # Generate v4 boot images for gzip and lz4 kernel compression. 413 if slot == "a": 414 for compression in ['gz', 'lz4']: 415 out = out_dir / f"boot_v4_{compression}_{slot}.img" 416 # Replace kernel 417 common[2] = out_dir / f"gki_boot_{compression}_kernel" 418 419 subprocess.run( 420 common + ["--header_version", "4", "-o", out], 421 check=True, 422 stderr=subprocess.STDOUT, 423 ) 424 425 # Generates init_boot 426 subprocess.run( 427 [ 428 MKBOOTIMG_TOOL, 429 "-o", 430 out_dir / f"init_boot_{slot}.img", 431 "--ramdisk", 432 generic_ramdisk, 433 # init_boot uses fixed version 4. 434 "--header_version", 435 "4", 436 ], 437 check=True, 438 stderr=subprocess.STDOUT, 439 ) 440 441 # Generates vendor_boot images 442 common = [ 443 MKBOOTIMG_TOOL, 444 "--vendor_cmdline", 445 vendor_cmdline, 446 "--vendor_ramdisk", 447 vendor_ramdisk, 448 "--dtb", 449 out_dir / "device_tree.dtb", 450 ] 451 # Generates vendor_boot v3 (no bootconfig) 452 subprocess.run( 453 common 454 + [ 455 "--dtb", 456 out_dir / "device_tree.dtb", 457 "--vendor_boot", 458 out_dir / f"vendor_boot_v3_{slot}.img", 459 "--header_version", 460 "3", 461 ], 462 stderr=subprocess.STDOUT, 463 check=True, 464 ) 465 # Generates vendor_boot v4 466 subprocess.run( 467 common 468 + [ 469 "--dtb", 470 out_dir / "device_tree.dtb", 471 "--vendor_boot", 472 out_dir / f"vendor_boot_v4_{slot}.img", 473 "--vendor_bootconfig", 474 vendor_bootconfig, 475 "--header_version", 476 "4", 477 ], 478 stderr=subprocess.STDOUT, 479 check=True, 480 ) 481 # Generates vendor_boot v4 with dttable structure 482 subprocess.run( 483 common 484 + [ 485 "--dtb", 486 out_dir / "dtb.img", 487 "--vendor_boot", 488 out_dir / f"vendor_boot_v4_dttable_{slot}.img", 489 "--vendor_bootconfig", 490 vendor_bootconfig, 491 "--header_version", 492 "4", 493 ], 494 stderr=subprocess.STDOUT, 495 check=True, 496 ) 497 498 # Generates a vbmeta data for v0 - v2 setup 499 for i in [0, 1, 2]: 500 parts = [ 501 (f"boot", out_dir / f"boot_v{i}_{slot}.img"), 502 ("dtbo", out_dir / f"dtbo_{slot}.img"), 503 ("dtb", out_dir / f"dtb_{slot}.img"), 504 ] 505 gen_android_test_vbmeta( 506 parts, out_dir / f"vbmeta_v{i}_{slot}.img" 507 ) 508 509 # Generates different combinations of v3/v4 boot/vendor_boot/init_boot setup. 510 for use_init_boot in [True, False]: 511 for boot_ver in [3, 4]: 512 if use_init_boot: 513 boot = ( 514 out_dir / f"boot_no_ramdisk_v{boot_ver}_{slot}.img" 515 ) 516 else: 517 boot = out_dir / f"boot_v{boot_ver}_{slot}.img" 518 519 for vendor_ver in [3, 4]: 520 vendor_boot = ( 521 out_dir / f"vendor_boot_v{vendor_ver}_{slot}.img" 522 ) 523 524 parts = [ 525 (f"boot", boot), 526 (f"vendor_boot", vendor_boot), 527 ("dtbo", out_dir / f"dtbo_{slot}.img"), 528 ("dtb", out_dir / f"dtb_{slot}.img"), 529 ] 530 prefix = f"vbmeta_v{boot_ver}_v{vendor_ver}" 531 if use_init_boot: 532 vbmeta_out = prefix + f"_init_boot_{slot}.img" 533 parts += [ 534 ( 535 "init_boot", 536 out_dir / f"init_boot_{slot}.img", 537 ) 538 ] 539 else: 540 vbmeta_out = prefix + f"_{slot}.img" 541 542 gen_android_test_vbmeta(parts, out_dir / vbmeta_out) 543 544 # Generate v4 vbmeta image for vendor_boot with dttable structure 545 vbmeta_out = out_dir / \ 546 f"vbmeta_v4_dttable_{slot}.img" 547 parts = [ 548 (f"boot", out_dir / 549 f"boot_v4_{slot}.img"), 550 (f"vendor_boot", out_dir / 551 f"vendor_boot_v4_dttable_{slot}.img"), 552 ("dtbo", out_dir / f"dtbo_{slot}.img"), 553 ("dtb", out_dir / f"dtb_{slot}.img"), 554 ] 555 gen_android_test_vbmeta(parts, vbmeta_out) 556 557 # Generate v4 vbmeta images for both gzip and lz4 kernel compression. 558 if slot == "a": 559 for compression in ["gz", "lz4"]: 560 vbmeta_out = out_dir / \ 561 f"vbmeta_v4_{compression}_{slot}.img" 562 parts = [ 563 (f"boot", out_dir / 564 f"boot_v4_{compression}_{slot}.img"), 565 (f"vendor_boot", out_dir / 566 f"vendor_boot_v4_{slot}.img"), 567 ("dtbo", out_dir / f"dtbo_{slot}.img"), 568 ("dtb", out_dir / f"dtb_{slot}.img"), 569 ] 570 gen_android_test_vbmeta(parts, vbmeta_out) 571 572 573def gen_zircon_test_images(zbi_tool): 574 if not zbi_tool: 575 print( 576 "Warning: ZBI tool not provided. Skip regenerating zircon test" 577 " images" 578 ) 579 return 580 581 ATX_METADATA = AVB_TEST_DATA_DIR / "cert_metadata.bin" 582 583 with tempfile.TemporaryDirectory() as temp_dir: 584 for slot in ["a", "b", "r", "slotless"]: 585 temp_dir = pathlib.Path(temp_dir) 586 random.seed(RNG_SEED_ZIRCON[slot]) 587 out_kernel_bin_file = temp_dir / f"zircon_{slot}.bin" 588 # The first 16 bytes are two u64 integers representing `entry` and 589 # `reserve_memory_size`. 590 # Set `entry` value to 2048 and `reserve_memory_size` to 1024. 591 kernel_bytes = int(2048).to_bytes(8, "little") + int(1024).to_bytes( 592 8, "little" 593 ) 594 kernel_bytes += random.randbytes(1 * SZ_KB - 16) 595 out_kernel_bin_file.write_bytes(kernel_bytes) 596 out_zbi_file = SCRIPT_DIR / f"zircon_{slot}.zbi" 597 # Puts image in a zbi container. 598 subprocess.run( 599 [ 600 zbi_tool, 601 "--output", 602 out_zbi_file, 603 "--type=KERNEL_X64", 604 out_kernel_bin_file, 605 ] 606 ) 607 608 # Generates vbmeta descriptor. 609 vbmeta_desc = f"{temp_dir}/zircon_{slot}.vbmeta.desc" 610 subprocess.run( 611 [ 612 AVB_TOOL, 613 "add_hash_footer", 614 "--image", 615 out_zbi_file, 616 "--partition_name", 617 "zircon", 618 "--do_not_append_vbmeta_image", 619 "--output_vbmeta_image", 620 vbmeta_desc, 621 "--partition_size", 622 "209715200", 623 ] 624 ) 625 # Generates two cmdline ZBI items to add as property descriptors to 626 # vbmeta image for test. 627 vbmeta_prop_args = [] 628 for i in range(2): 629 prop_zbi_payload = f"{temp_dir}/prop_zbi_payload_{i}.bin" 630 subprocess.run( 631 [ 632 zbi_tool, 633 "--output", 634 prop_zbi_payload, 635 "--type=CMDLINE", 636 f"--entry=vb_prop_{i}=val", 637 ] 638 ) 639 vbmeta_prop_args += [ 640 "--prop_from_file", 641 f"zbi_vb_prop_{i}:{prop_zbi_payload}", 642 ] 643 # Also adds a property where the key name does not starts with 644 # "zbi". The item should not be processed. 645 vbmeta_prop_args += [ 646 "--prop_from_file", 647 f"vb_prop_{i}:{prop_zbi_payload}", 648 ] 649 # Generates vbmeta image 650 vbmeta_img = SCRIPT_DIR / f"vbmeta_{slot}.bin" 651 subprocess.run( 652 [ 653 AVB_TOOL, 654 "make_vbmeta_image", 655 "--output", 656 vbmeta_img, 657 "--key", 658 PSK, 659 "--algorithm", 660 "SHA512_RSA4096", 661 "--public_key_metadata", 662 ATX_METADATA, 663 "--include_descriptors_from_image", 664 vbmeta_desc, 665 "--rollback_index", 666 f"{TEST_ROLLBACK_INDEX}", 667 "--rollback_index_location", 668 f"{TEST_ROLLBACK_INDEX_LOCATION}", 669 ] 670 + vbmeta_prop_args 671 ) 672 673 674# Generates test data for A/B slot Manager writeback test 675def gen_writeback_test_bin(): 676 subprocess.run( 677 [ 678 GPT_TOOL, 679 SCRIPT_DIR / "writeback_test_disk.bin", 680 "64K", 681 "--partition=test_partition,4k,/dev/zero", 682 ], 683 check=True, 684 ) 685 686 687def sha256_hash(path: pathlib.Path) -> bytes: 688 """Returns the SHA256 hash of the given file.""" 689 hash_hex = ( 690 subprocess.run( 691 ["sha256sum", path], 692 check=True, 693 capture_output=True, 694 text=True, 695 ) 696 .stdout.split()[0] # output is "<hash> <filename>". 697 .strip() 698 ) 699 return bytes.fromhex(hash_hex) 700 701 702def gen_vbmeta(): 703 """Creates the vbmeta keys and signs some images.""" 704 # Use the test vbmeta keys from libavb. 705 for name in [ 706 "testkey_rsa4096.pem", 707 "testkey_rsa4096_pub.pem", 708 "testkey_cert_psk.pem", 709 "cert_metadata.bin", 710 "cert_permanent_attributes.bin", 711 ]: 712 shutil.copyfile(AVB_TEST_DATA_DIR / name, SCRIPT_DIR / name) 713 714 # We need the permanent attribute SHA256 hash for libavb_cert callbacks. 715 hash_bytes = sha256_hash(SCRIPT_DIR / "cert_permanent_attributes.bin") 716 (SCRIPT_DIR / "cert_permanent_attributes.hash").write_bytes(hash_bytes) 717 718 # Also creates a corrupted version of the permanent attributes to test failure. 719 # This is a little bit of a pain but we don't have an easy way to do a SHA256 in Rust 720 # at the moment so we can't generate it on the fly. 721 bad_attrs = bytearray( 722 (SCRIPT_DIR / "cert_permanent_attributes.bin").read_bytes() 723 ) 724 bad_attrs[4] ^= 0x01 # Bytes 0-3 = version, byte 4 starts the public key. 725 (SCRIPT_DIR / "cert_permanent_attributes.bad.bin").write_bytes(bad_attrs) 726 hash_bytes = sha256_hash(SCRIPT_DIR / "cert_permanent_attributes.bad.bin") 727 (SCRIPT_DIR / "cert_permanent_attributes.bad.hash").write_bytes(hash_bytes) 728 729 # Convert the public key to raw bytes for use in verification. 730 subprocess.run( 731 [ 732 AVB_TOOL, 733 "extract_public_key", 734 "--key", 735 SCRIPT_DIR / "testkey_rsa4096_pub.pem", 736 "--output", 737 SCRIPT_DIR / "testkey_rsa4096_pub.bin", 738 ], 739 check=True, 740 ) 741 742 with tempfile.TemporaryDirectory() as temp_dir: 743 temp_dir = pathlib.Path(temp_dir) 744 745 # Create the hash descriptor. We only need this temporarily until we add 746 # it into the final vbmeta image. 747 hash_descriptor_path = temp_dir / "hash_descriptor.bin" 748 subprocess.run( 749 [ 750 AVB_TOOL, 751 "add_hash_footer", 752 "--dynamic_partition_size", 753 "--do_not_append_vbmeta_image", 754 "--partition_name", 755 "zircon_a", 756 "--image", 757 SCRIPT_DIR / "zircon_a.zbi", 758 "--output_vbmeta_image", 759 hash_descriptor_path, 760 "--salt", 761 "2000", 762 ], 763 check=True, 764 ) 765 766 # Create the final signed vbmeta including the hash descriptor. 767 subprocess.run( 768 [ 769 AVB_TOOL, 770 "make_vbmeta_image", 771 "--key", 772 SCRIPT_DIR / "testkey_rsa4096.pem", 773 "--algorithm", 774 "SHA512_RSA4096", 775 "--include_descriptors_from_image", 776 hash_descriptor_path, 777 "--output", 778 SCRIPT_DIR / "zircon_a.vbmeta", 779 ], 780 check=True, 781 ) 782 783 # Also creates a vbmeta using the libavb_cert extension. 784 subprocess.run( 785 [ 786 AVB_TOOL, 787 "make_vbmeta_image", 788 "--key", 789 SCRIPT_DIR / "testkey_cert_psk.pem", 790 "--public_key_metadata", 791 SCRIPT_DIR / "cert_metadata.bin", 792 "--algorithm", 793 "SHA512_RSA4096", 794 "--include_descriptors_from_image", 795 hash_descriptor_path, 796 "--output", 797 SCRIPT_DIR / "zircon_a.vbmeta.cert", 798 ] 799 ) 800 801 802def _parse_args() -> argparse.Namespace: 803 parser = argparse.ArgumentParser( 804 description=__doc__, 805 formatter_class=argparse.RawDescriptionHelpFormatter, 806 ) 807 808 parser.add_argument( 809 "--zbi_tool", default="", help="Path to the Fuchsia ZBI tool" 810 ) 811 812 return parser.parse_args() 813 814 815if __name__ == "__main__": 816 args = _parse_args() 817 gen_writeback_test_bin() 818 gen_sparse_test_file() 819 gen_zircon_test_images(args.zbi_tool) 820 gen_vbmeta() 821 gen_android_test_images() 822