1#!/usr/bin/env python 2# 3# Copyright (C) 2008 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 17""" 18Signs all the APK files in a target-files zipfile, producing a new 19target-files zip. 20 21Usage: sign_target_files_apks [flags] input_target_files output_target_files 22 23 -e (--extra_apks) <name,name,...=key> 24 Add extra APK/APEX name/key pairs as though they appeared in apkcerts.txt 25 or apexkeys.txt (so mappings specified by -k and -d are applied). Keys 26 specified in -e override any value for that app contained in the 27 apkcerts.txt file, or the container key for an APEX. Option may be 28 repeated to give multiple extra packages. 29 30 --extra_apex_payload_key <name,name,...=key> 31 Add a mapping for APEX package name to payload signing key, which will 32 override the default payload signing key in apexkeys.txt. Note that the 33 container key should be overridden via the `--extra_apks` flag above. 34 Option may be repeated for multiple APEXes. 35 36 --skip_apks_with_path_prefix <prefix> 37 Skip signing an APK if it has the matching prefix in its path. The prefix 38 should be matching the entry name, which has partition names in upper 39 case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be 40 repeated to give multiple prefixes. 41 42 -k (--key_mapping) <src_key=dest_key> 43 Add a mapping from the key name as specified in apkcerts.txt (the 44 src_key) to the real key you wish to sign the package with 45 (dest_key). Option may be repeated to give multiple key 46 mappings. 47 48 -d (--default_key_mappings) <dir> 49 Set up the following key mappings: 50 51 $devkey/devkey ==> $dir/releasekey 52 $devkey/testkey ==> $dir/releasekey 53 $devkey/media ==> $dir/media 54 $devkey/shared ==> $dir/shared 55 $devkey/platform ==> $dir/platform 56 57 where $devkey is the directory part of the value of 58 default_system_dev_certificate from the input target-files's 59 META/misc_info.txt. (Defaulting to "build/make/target/product/security" 60 if the value is not present in misc_info. 61 62 -d and -k options are added to the set of mappings in the order 63 in which they appear on the command line. 64 65 -o (--replace_ota_keys) 66 Replace the certificate (public key) used by OTA package verification 67 with the ones specified in the input target_files zip (in the 68 META/otakeys.txt file). Key remapping (-k and -d) is performed on the 69 keys. For A/B devices, the payload verification key will be replaced 70 as well. If there're multiple OTA keys, only the first one will be used 71 for payload verification. 72 73 -t (--tag_changes) <+tag>,<-tag>,... 74 Comma-separated list of changes to make to the set of tags (in 75 the last component of the build fingerprint). Prefix each with 76 '+' or '-' to indicate whether that tag should be added or 77 removed. Changes are processed in the order they appear. 78 Default value is "-test-keys,-dev-keys,+release-keys". 79 80 --replace_verity_private_key <key> 81 Replace the private key used for verity signing. It expects a filename 82 WITHOUT the extension (e.g. verity_key). 83 84 --replace_verity_public_key <key> 85 Replace the certificate (public key) used for verity verification. The 86 key file replaces the one at BOOT/RAMDISK/verity_key. It expects the key 87 filename WITH the extension (e.g. verity_key.pub). 88 89 --replace_verity_keyid <path_to_X509_PEM_cert_file> 90 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip 91 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>. 92 93 --remove_avb_public_keys <key1>,<key2>,... 94 Remove AVB public keys from the first-stage ramdisk. The key file to 95 remove is located at either of the following dirs: 96 - BOOT/RAMDISK/avb/ or 97 - BOOT/RAMDISK/first_stage_ramdisk/avb/ 98 The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is 99 set to true. 100 101 --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta, 102 vbmeta_system,vbmeta_vendor}_algorithm <algorithm> 103 --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta, 104 vbmeta_system,vbmeta_vendor}_key <key> 105 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign 106 the specified image. Otherwise it uses the existing values in info dict. 107 108 --avb_{apex,init_boot,boot,recovery,system,system_other,vendor,dtbo,vbmeta, 109 vbmeta_system,vbmeta_vendor}_extra_args <args> 110 Specify any additional args that are needed to AVB-sign the image 111 (e.g. "--signing_helper /path/to/helper"). The args will be appended to 112 the existing ones in info dict. 113 114 --avb_extra_custom_image_key <partition=key> 115 --avb_extra_custom_image_algorithm <partition=algorithm> 116 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign 117 the specified custom images mounted on the partition. Otherwise it uses 118 the existing values in info dict. 119 120 --avb_extra_custom_image_extra_args <partition=extra_args> 121 Specify any additional args that are needed to AVB-sign the custom images 122 mounted on the partition (e.g. "--signing_helper /path/to/helper"). The 123 args will be appended to the existing ones in info dict. 124 125 --gki_signing_algorithm <algorithm> 126 --gki_signing_key <key> 127 --gki_signing_extra_args <args> 128 DEPRECATED Does nothing. 129 130 --android_jar_path <path> 131 Path to the android.jar to repack the apex file. 132 133 --allow_gsi_debug_sepolicy 134 Allow the existence of the file 'userdebug_plat_sepolicy.cil' under 135 (/system/system_ext|/system_ext)/etc/selinux. 136 If not set, error out when the file exists. 137 138 --override_apk_keys <path> 139 Replace all APK keys with this private key 140 141 --override_apex_keys <path> 142 Replace all APEX keys with this private key 143 144 -k (--package_key) <key> 145 Key to use to sign the package (default is the value of 146 default_system_dev_certificate from the input target-files's 147 META/misc_info.txt, or "build/make/target/product/security/testkey" if 148 that value is not specified). 149 150 For incremental OTAs, the default value is based on the source 151 target-file, not the target build. 152 153 --payload_signer <signer> 154 Specify the signer when signing the payload and metadata for A/B OTAs. 155 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign 156 with the package private key. If the private key cannot be accessed 157 directly, a payload signer that knows how to do that should be specified. 158 The signer will be supplied with "-inkey <path_to_key>", 159 "-in <input_file>" and "-out <output_file>" parameters. 160 161 --payload_signer_args <args> 162 Specify the arguments needed for payload signer. 163 164 --payload_signer_maximum_signature_size <signature_size> 165 The maximum signature size (in bytes) that would be generated by the given 166 payload signer. Only meaningful when custom payload signer is specified 167 via '--payload_signer'. 168 If the signer uses a RSA key, this should be the number of bytes to 169 represent the modulus. If it uses an EC key, this is the size of a 170 DER-encoded ECDSA signature. 171""" 172 173from __future__ import print_function 174 175import base64 176import copy 177import errno 178import gzip 179import io 180import itertools 181import logging 182import os 183import re 184import shutil 185import stat 186import sys 187import shlex 188import tempfile 189import zipfile 190from xml.etree import ElementTree 191 192import add_img_to_target_files 193import ota_from_raw_img 194import apex_utils 195import common 196import payload_signer 197import update_payload 198from payload_signer import SignOtaPackage, PAYLOAD_BIN 199 200 201if sys.hexversion < 0x02070000: 202 print("Python 2.7 or newer is required.", file=sys.stderr) 203 sys.exit(1) 204 205 206logger = logging.getLogger(__name__) 207 208OPTIONS = common.OPTIONS 209 210OPTIONS.extra_apks = {} 211OPTIONS.extra_apex_payload_keys = {} 212OPTIONS.skip_apks_with_path_prefix = set() 213OPTIONS.key_map = {} 214OPTIONS.rebuild_recovery = False 215OPTIONS.replace_ota_keys = False 216OPTIONS.remove_avb_public_keys = None 217OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys") 218OPTIONS.avb_keys = {} 219OPTIONS.avb_algorithms = {} 220OPTIONS.avb_extra_args = {} 221OPTIONS.android_jar_path = None 222OPTIONS.vendor_partitions = set() 223OPTIONS.vendor_otatools = None 224OPTIONS.allow_gsi_debug_sepolicy = False 225OPTIONS.override_apk_keys = None 226OPTIONS.override_apex_keys = None 227OPTIONS.input_tmp = None 228 229 230AVB_FOOTER_ARGS_BY_PARTITION = { 231 'boot': 'avb_boot_add_hash_footer_args', 232 'init_boot': 'avb_init_boot_add_hash_footer_args', 233 'dtbo': 'avb_dtbo_add_hash_footer_args', 234 'product': 'avb_product_add_hashtree_footer_args', 235 'recovery': 'avb_recovery_add_hash_footer_args', 236 'system': 'avb_system_add_hashtree_footer_args', 237 'system_dlkm': "avb_system_dlkm_add_hashtree_footer_args", 238 'system_ext': 'avb_system_ext_add_hashtree_footer_args', 239 'system_other': 'avb_system_other_add_hashtree_footer_args', 240 'odm': 'avb_odm_add_hashtree_footer_args', 241 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args', 242 'pvmfw': 'avb_pvmfw_add_hash_footer_args', 243 'vendor': 'avb_vendor_add_hashtree_footer_args', 244 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args', 245 'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args', 246 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args", 247 'vbmeta': 'avb_vbmeta_args', 248 'vbmeta_system': 'avb_vbmeta_system_args', 249 'vbmeta_vendor': 'avb_vbmeta_vendor_args', 250} 251 252 253# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS. 254for partition in common.AVB_PARTITIONS: 255 if partition not in AVB_FOOTER_ARGS_BY_PARTITION: 256 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition)) 257 258# Partitions that can be regenerated after signing using a separate 259# vendor otatools package. 260ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"]) 261 262 263def IsApexFile(filename): 264 return filename.endswith(".apex") or filename.endswith(".capex") 265 266 267def IsOtaPackage(fp): 268 with zipfile.ZipFile(fp) as zfp: 269 if not PAYLOAD_BIN in zfp.namelist(): 270 return False 271 with zfp.open(PAYLOAD_BIN, "r") as payload: 272 magic = payload.read(4) 273 return magic == b"CrAU" 274 275 276def IsEntryOtaPackage(input_zip, filename): 277 with input_zip.open(filename, "r") as fp: 278 external_attr = input_zip.getinfo(filename).external_attr 279 if stat.S_ISLNK(external_attr >> 16): 280 return IsEntryOtaPackage(input_zip, 281 os.path.join(os.path.dirname(filename), fp.read().decode())) 282 return IsOtaPackage(fp) 283 284 285def GetApexFilename(filename): 286 name = os.path.basename(filename) 287 # Replace the suffix for compressed apex 288 if name.endswith(".capex"): 289 return name.replace(".capex", ".apex") 290 return name 291 292 293def GetApkCerts(certmap): 294 if OPTIONS.override_apk_keys is not None: 295 for apk in certmap.keys(): 296 certmap[apk] = OPTIONS.override_apk_keys 297 298 # apply the key remapping to the contents of the file 299 for apk, cert in certmap.items(): 300 certmap[apk] = OPTIONS.key_map.get(cert, cert) 301 302 # apply all the -e options, overriding anything in the file 303 for apk, cert in OPTIONS.extra_apks.items(): 304 if not cert: 305 cert = "PRESIGNED" 306 certmap[apk] = OPTIONS.key_map.get(cert, cert) 307 308 return certmap 309 310 311def GetApexKeys(keys_info, key_map): 312 """Gets APEX payload and container signing keys by applying the mapping rules. 313 314 Presigned payload / container keys will be set accordingly. 315 316 Args: 317 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key, 318 container_key, sign_tool). 319 key_map: A dict that overrides the keys, specified via command-line input. 320 321 Returns: 322 A dict that contains the updated APEX key mapping, which should be used for 323 the current signing. 324 325 Raises: 326 AssertionError: On invalid container / payload key overrides. 327 """ 328 if OPTIONS.override_apex_keys is not None: 329 for apex in keys_info.keys(): 330 keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2]) 331 332 if OPTIONS.override_apk_keys is not None: 333 key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys) 334 for apex in keys_info.keys(): 335 keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2]) 336 337 # Apply all the --extra_apex_payload_key options to override the payload 338 # signing keys in the given keys_info. 339 for apex, key in OPTIONS.extra_apex_payload_keys.items(): 340 if not key: 341 key = 'PRESIGNED' 342 if apex not in keys_info: 343 logger.warning('Failed to find %s in target_files; Ignored', apex) 344 continue 345 keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2]) 346 347 # Apply the key remapping to container keys. 348 for apex, (payload_key, container_key, sign_tool) in keys_info.items(): 349 keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool) 350 351 # Apply all the --extra_apks options to override the container keys. 352 for apex, key in OPTIONS.extra_apks.items(): 353 # Skip non-APEX containers. 354 if apex not in keys_info: 355 continue 356 if not key: 357 key = 'PRESIGNED' 358 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2]) 359 360 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the 361 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload 362 # (overridden via commandline) indicates a config error, which should not be 363 # allowed. 364 for apex, (payload_key, container_key, sign_tool) in keys_info.items(): 365 if container_key != 'PRESIGNED': 366 continue 367 if apex in OPTIONS.extra_apex_payload_keys: 368 payload_override = OPTIONS.extra_apex_payload_keys[apex] 369 assert payload_override == '', \ 370 ("Invalid APEX key overrides: {} has PRESIGNED container but " 371 "non-PRESIGNED payload key {}").format(apex, payload_override) 372 if payload_key != 'PRESIGNED': 373 print( 374 "Setting {} payload as PRESIGNED due to PRESIGNED container".format( 375 apex)) 376 keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None) 377 378 return keys_info 379 380 381def GetApkFileInfo(filename, compressed_extension, skipped_prefixes): 382 """Returns the APK info based on the given filename. 383 384 Checks if the given filename (with path) looks like an APK file, by taking the 385 compressed extension into consideration. If it appears to be an APK file, 386 further checks if the APK file should be skipped when signing, based on the 387 given path prefixes. 388 389 Args: 390 filename: Path to the file. 391 compressed_extension: The extension string of compressed APKs (e.g. ".gz"), 392 or None if there's no compressed APKs. 393 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped. 394 395 Returns: 396 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the 397 given filename is an APK file. is_compressed indicates whether the APK file 398 is compressed (only meaningful when is_apk is True). should_be_skipped 399 indicates whether the filename matches any of the given prefixes to be 400 skipped. 401 402 Raises: 403 AssertionError: On invalid compressed_extension or skipped_prefixes inputs. 404 """ 405 assert compressed_extension is None or compressed_extension.startswith('.'), \ 406 "Invalid compressed_extension arg: '{}'".format(compressed_extension) 407 408 # skipped_prefixes should be one of set/list/tuple types. Other types such as 409 # str shouldn't be accepted. 410 assert isinstance(skipped_prefixes, (set, list, tuple)), \ 411 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes)) 412 413 compressed_apk_extension = ( 414 ".apk" + compressed_extension if compressed_extension else None) 415 is_apk = (filename.endswith(".apk") or 416 (compressed_apk_extension and 417 filename.endswith(compressed_apk_extension))) 418 if not is_apk: 419 return (False, False, False) 420 421 is_compressed = (compressed_apk_extension and 422 filename.endswith(compressed_apk_extension)) 423 should_be_skipped = filename.startswith(tuple(skipped_prefixes)) 424 return (True, is_compressed, should_be_skipped) 425 426 427def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys, 428 compressed_extension, apex_keys): 429 """Checks that all the APKs and APEXes have keys specified. 430 431 Args: 432 input_tf_zip: An open target_files zip file. 433 known_keys: A set of APKs and APEXes that have known signing keys. 434 compressed_extension: The extension string of compressed APKs, such as 435 '.gz', or None if there's no compressed APKs. 436 apex_keys: A dict that contains the key mapping from APEX name to 437 (payload_key, container_key, sign_tool). 438 439 Raises: 440 AssertionError: On finding unknown APKs and APEXes. 441 """ 442 unknown_files = [] 443 for info in input_tf_zip.infolist(): 444 # Handle APEXes on all partitions 445 if IsApexFile(info.filename): 446 name = GetApexFilename(info.filename) 447 if name not in known_keys: 448 unknown_files.append(name) 449 continue 450 451 # And APKs. 452 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( 453 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) 454 if not is_apk or should_be_skipped: 455 continue 456 457 name = os.path.basename(info.filename) 458 if is_compressed: 459 name = name[:-len(compressed_extension)] 460 if name not in known_keys: 461 unknown_files.append(name) 462 463 assert not unknown_files, \ 464 ("No key specified for:\n {}\n" 465 "Use '-e <apkname>=' to specify a key (which may be an empty string to " 466 "not sign this apk).".format("\n ".join(unknown_files))) 467 468 # For all the APEXes, double check that we won't have an APEX that has only 469 # one of the payload / container keys set. Note that non-PRESIGNED container 470 # with PRESIGNED payload could be allowed but currently unsupported. It would 471 # require changing SignApex implementation. 472 if not apex_keys: 473 return 474 475 invalid_apexes = [] 476 for info in input_tf_zip.infolist(): 477 if not IsApexFile(info.filename): 478 continue 479 480 name = GetApexFilename(info.filename) 481 482 (payload_key, container_key, _) = apex_keys[name] 483 if ((payload_key in common.SPECIAL_CERT_STRINGS and 484 container_key not in common.SPECIAL_CERT_STRINGS) or 485 (payload_key not in common.SPECIAL_CERT_STRINGS and 486 container_key in common.SPECIAL_CERT_STRINGS)): 487 invalid_apexes.append( 488 "{}: payload_key {}, container_key {}".format( 489 name, payload_key, container_key)) 490 491 assert not invalid_apexes, \ 492 "Invalid APEX keys specified:\n {}\n".format( 493 "\n ".join(invalid_apexes)) 494 495 496def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, 497 is_compressed, apk_name): 498 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name) 499 unsigned.write(data) 500 unsigned.flush() 501 502 if is_compressed: 503 uncompressed = tempfile.NamedTemporaryFile() 504 with gzip.open(unsigned.name, "rb") as in_file, \ 505 open(uncompressed.name, "wb") as out_file: 506 shutil.copyfileobj(in_file, out_file) 507 508 # Finally, close the "unsigned" file (which is gzip compressed), and then 509 # replace it with the uncompressed version. 510 # 511 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use, 512 # we could just gzip / gunzip in-memory buffers instead. 513 unsigned.close() 514 unsigned = uncompressed 515 516 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name) 517 518 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's 519 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK 520 # didn't change, we don't want its signature to change due to the switch 521 # from SHA-1 to SHA-256. 522 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion 523 # is 18 or higher. For pre-N builds we disable this mechanism by pretending 524 # that the APK's minSdkVersion is 1. 525 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to 526 # determine whether to use SHA-256. 527 min_api_level = None 528 if platform_api_level > 23: 529 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's 530 # minSdkVersion attribute 531 min_api_level = None 532 else: 533 # Force APK signer to use SHA-1 534 min_api_level = 1 535 536 common.SignFile(unsigned.name, signed.name, keyname, pw, 537 min_api_level=min_api_level, 538 codename_to_api_level_map=codename_to_api_level_map) 539 540 data = None 541 if is_compressed: 542 # Recompress the file after it has been signed. 543 compressed = tempfile.NamedTemporaryFile() 544 with open(signed.name, "rb") as in_file, \ 545 gzip.open(compressed.name, "wb") as out_file: 546 shutil.copyfileobj(in_file, out_file) 547 548 data = compressed.read() 549 compressed.close() 550 else: 551 data = signed.read() 552 553 unsigned.close() 554 signed.close() 555 556 return data 557 558 559 560def IsBuildPropFile(filename): 561 return filename in ( 562 "SYSTEM/etc/prop.default", 563 "BOOT/RAMDISK/prop.default", 564 "RECOVERY/RAMDISK/prop.default", 565 566 "VENDOR_BOOT/RAMDISK/default.prop", 567 "VENDOR_BOOT/RAMDISK/prop.default", 568 569 # ROOT/default.prop is a legacy path, but may still exist for upgrading 570 # devices that don't support `property_overrides_split_enabled`. 571 "ROOT/default.prop", 572 573 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist 574 # as a symlink in the current code. So it's a no-op here. Keeping the 575 # path here for clarity. 576 # Some build props might be stored under path 577 # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and 578 # default.prop can be a symbolic link to prop.default, so overwrite all 579 # files that ends with build.prop, default.prop or prop.default 580 "RECOVERY/RAMDISK/default.prop") or \ 581 filename.endswith("build.prop") or \ 582 filename.endswith("/default.prop") or \ 583 filename.endswith("/prop.default") 584 585 586def GetOtaSigningArgs(): 587 args = [] 588 if OPTIONS.package_key: 589 args.extend(["--package_key", OPTIONS.package_key]) 590 if OPTIONS.payload_signer: 591 args.extend(["--payload_signer=" + OPTIONS.payload_signer]) 592 if OPTIONS.payload_signer_args: 593 args.extend(["--payload_signer_args=" + shlex.join(OPTIONS.payload_signer_args)]) 594 if OPTIONS.search_path: 595 args.extend(["--search_path", OPTIONS.search_path]) 596 if OPTIONS.payload_signer_maximum_signature_size: 597 args.extend(["--payload_signer_maximum_signature_size", 598 OPTIONS.payload_signer_maximum_signature_size]) 599 if OPTIONS.private_key_suffix: 600 args.extend(["--private_key_suffix", OPTIONS.private_key_suffix]) 601 return args 602 603 604def RegenerateKernelPartitions(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info): 605 """Re-generate boot and dtbo partitions using new signing configuration""" 606 files_to_unzip = [ 607 "PREBUILT_IMAGES/*", "BOOTABLE_IMAGES/*.img", "*/boot_16k.img", "*/dtbo_16k.img"] 608 if OPTIONS.input_tmp is None: 609 OPTIONS.input_tmp = common.UnzipTemp(input_tf_zip.filename, files_to_unzip) 610 else: 611 common.UnzipToDir(input_tf_zip.filename, OPTIONS.input_tmp, files_to_unzip) 612 unzip_dir = OPTIONS.input_tmp 613 os.makedirs(os.path.join(unzip_dir, "IMAGES"), exist_ok=True) 614 615 boot_image = common.GetBootableImage( 616 "IMAGES/boot.img", "boot.img", unzip_dir, "BOOT", misc_info) 617 if boot_image: 618 boot_image.WriteToDir(unzip_dir) 619 boot_image = os.path.join(unzip_dir, boot_image.name) 620 common.ZipWrite(output_tf_zip, boot_image, "IMAGES/boot.img", 621 compress_type=zipfile.ZIP_STORED) 622 if misc_info.get("has_dtbo") == "true": 623 add_img_to_target_files.AddDtbo(output_tf_zip) 624 return unzip_dir 625 626 627def RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, filename, input_ota): 628 with input_tf_zip.open(filename, "r") as in_fp: 629 payload = update_payload.Payload(in_fp) 630 is_incremental = any([part.HasField('old_partition_info') 631 for part in payload.manifest.partitions]) 632 is_boot_ota = filename.startswith( 633 "VENDOR/boot_otas/") or filename.startswith("SYSTEM/boot_otas/") 634 if not is_boot_ota: 635 return 636 is_4k_boot_ota = filename in [ 637 "VENDOR/boot_otas/boot_ota_4k.zip", "SYSTEM/boot_otas/boot_ota_4k.zip"] 638 # Only 4K boot image is re-generated, so if 16K boot ota isn't incremental, 639 # we do not need to re-generate 640 if not is_4k_boot_ota and not is_incremental: 641 return 642 643 timestamp = str(payload.manifest.max_timestamp) 644 partitions = [part.partition_name for part in payload.manifest.partitions] 645 unzip_dir = OPTIONS.input_tmp 646 signed_boot_image = os.path.join(unzip_dir, "IMAGES", "boot.img") 647 if not os.path.exists(signed_boot_image): 648 logger.warn("Need to re-generate boot OTA {} but failed to get signed boot image. 16K dev option will be impacted, after rolling back to 4K user would need to sideload/flash their device to continue receiving OTAs.") 649 return 650 signed_dtbo_image = os.path.join(unzip_dir, "IMAGES", "dtbo.img") 651 if "dtbo" in partitions and not os.path.exists(signed_dtbo_image): 652 raise ValueError( 653 "Boot OTA {} has dtbo partition, but no dtbo image found in target files.".format(filename)) 654 if is_incremental: 655 signed_16k_boot_image = os.path.join( 656 unzip_dir, "IMAGES", "boot_16k.img") 657 signed_16k_dtbo_image = os.path.join( 658 unzip_dir, "IMAGES", "dtbo_16k.img") 659 if is_4k_boot_ota: 660 if os.path.exists(signed_16k_boot_image): 661 signed_boot_image = signed_16k_boot_image + ":" + signed_boot_image 662 if os.path.exists(signed_16k_dtbo_image): 663 signed_dtbo_image = signed_16k_dtbo_image + ":" + signed_dtbo_image 664 else: 665 if os.path.exists(signed_16k_boot_image): 666 signed_boot_image += ":" + signed_16k_boot_image 667 if os.path.exists(signed_16k_dtbo_image): 668 signed_dtbo_image += ":" + signed_16k_dtbo_image 669 670 args = ["ota_from_raw_img", 671 "--max_timestamp", timestamp, "--output", input_ota.name] 672 args.extend(GetOtaSigningArgs()) 673 if "dtbo" in partitions: 674 args.extend(["--partition_name", "boot,dtbo", 675 signed_boot_image, signed_dtbo_image]) 676 else: 677 args.extend(["--partition_name", "boot", signed_boot_image]) 678 logger.info( 679 "Re-generating boot OTA {} using cmd {}".format(filename, args)) 680 ota_from_raw_img.main(args) 681 682 683def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info, 684 apk_keys, apex_keys, key_passwords, 685 platform_api_level, codename_to_api_level_map, 686 compressed_extension): 687 # maxsize measures the maximum filename length, including the ones to be 688 # skipped. 689 try: 690 maxsize = max( 691 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() 692 if GetApkFileInfo(i.filename, compressed_extension, [])[0]]) 693 except ValueError: 694 # Sets this to zero for targets without APK files. 695 maxsize = 0 696 697 # Replace the AVB signing keys, if any. 698 ReplaceAvbSigningKeys(misc_info) 699 OPTIONS.info_dict = misc_info 700 701 # Rewrite the props in AVB signing args. 702 if misc_info.get('avb_enable') == 'true': 703 RewriteAvbProps(misc_info) 704 705 RegenerateKernelPartitions(input_tf_zip, output_tf_zip, misc_info) 706 707 for info in input_tf_zip.infolist(): 708 filename = info.filename 709 if filename.startswith("IMAGES/"): 710 continue 711 712 # Skip OTA-specific images (e.g. split super images), which will be 713 # re-generated during signing. 714 if filename.startswith("OTA/") and filename.endswith(".img"): 715 continue 716 717 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( 718 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) 719 data = input_tf_zip.read(filename) 720 out_info = copy.copy(info) 721 722 if is_apk and should_be_skipped: 723 # Copy skipped APKs verbatim. 724 print( 725 "NOT signing: %s\n" 726 " (skipped due to matching prefix)" % (filename,)) 727 common.ZipWriteStr(output_tf_zip, out_info, data) 728 729 # Sign APKs. 730 elif is_apk: 731 name = os.path.basename(filename) 732 if is_compressed: 733 name = name[:-len(compressed_extension)] 734 735 key = apk_keys[name] 736 if key not in common.SPECIAL_CERT_STRINGS: 737 print(" signing: %-*s (%s)" % (maxsize, name, key)) 738 signed_data = SignApk(data, key, key_passwords[key], platform_api_level, 739 codename_to_api_level_map, is_compressed, name) 740 common.ZipWriteStr(output_tf_zip, out_info, signed_data) 741 else: 742 # an APK we're not supposed to sign. 743 print( 744 "NOT signing: %s\n" 745 " (skipped due to special cert string)" % (name,)) 746 common.ZipWriteStr(output_tf_zip, out_info, data) 747 748 # Sign bundled APEX files on all partitions 749 elif IsApexFile(filename): 750 name = GetApexFilename(filename) 751 752 payload_key, container_key, sign_tool = apex_keys[name] 753 754 # We've asserted not having a case with only one of them PRESIGNED. 755 if (payload_key not in common.SPECIAL_CERT_STRINGS and 756 container_key not in common.SPECIAL_CERT_STRINGS): 757 print(" signing: %-*s container (%s)" % ( 758 maxsize, name, container_key)) 759 print(" : %-*s payload (%s)" % ( 760 maxsize, name, payload_key)) 761 762 signed_apex = apex_utils.SignApex( 763 misc_info['avb_avbtool'], 764 data, 765 payload_key, 766 container_key, 767 key_passwords, 768 apk_keys, 769 codename_to_api_level_map, 770 no_hashtree=None, # Let apex_util determine if hash tree is needed 771 signing_args=OPTIONS.avb_extra_args.get('apex'), 772 sign_tool=sign_tool) 773 common.ZipWrite(output_tf_zip, signed_apex, filename) 774 775 else: 776 print( 777 "NOT signing: %s\n" 778 " (skipped due to special cert string)" % (name,)) 779 common.ZipWriteStr(output_tf_zip, out_info, data) 780 781 elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename): 782 logger.info("Re-signing OTA package {}".format(filename)) 783 with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota: 784 RegenerateBootOTA(input_tf_zip, filename, input_ota) 785 786 SignOtaPackage(input_ota.name, output_ota.name) 787 common.ZipWrite(output_tf_zip, output_ota.name, filename, 788 compress_type=zipfile.ZIP_STORED) 789 # System properties. 790 elif IsBuildPropFile(filename): 791 print("Rewriting %s:" % (filename,)) 792 if stat.S_ISLNK(info.external_attr >> 16): 793 new_data = data 794 else: 795 new_data = RewriteProps(data.decode()) 796 common.ZipWriteStr(output_tf_zip, out_info, new_data) 797 798 # Replace the certs in *mac_permissions.xml (there could be multiple, such 799 # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml). 800 elif filename.endswith("mac_permissions.xml"): 801 print("Rewriting %s with new keys." % (filename,)) 802 new_data = ReplaceCerts(data.decode()) 803 common.ZipWriteStr(output_tf_zip, out_info, new_data) 804 805 # Ask add_img_to_target_files to rebuild the recovery patch if needed. 806 elif filename in ("SYSTEM/recovery-from-boot.p", 807 "VENDOR/recovery-from-boot.p", 808 809 "SYSTEM/etc/recovery.img", 810 "VENDOR/etc/recovery.img", 811 812 "SYSTEM/bin/install-recovery.sh", 813 "VENDOR/bin/install-recovery.sh"): 814 OPTIONS.rebuild_recovery = True 815 816 # Don't copy OTA certs if we're replacing them. 817 # Replacement of update-payload-key.pub.pem was removed in b/116660991. 818 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"): 819 pass 820 821 # Skip META/misc_info.txt since we will write back the new values later. 822 elif filename == "META/misc_info.txt": 823 pass 824 825 elif (OPTIONS.remove_avb_public_keys and 826 (filename.startswith("BOOT/RAMDISK/avb/") or 827 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))): 828 matched_removal = False 829 for key_to_remove in OPTIONS.remove_avb_public_keys: 830 if filename.endswith(key_to_remove): 831 matched_removal = True 832 print("Removing AVB public key from ramdisk: %s" % filename) 833 break 834 if not matched_removal: 835 # Copy it verbatim if we don't want to remove it. 836 common.ZipWriteStr(output_tf_zip, out_info, data) 837 838 # Skip the vbmeta digest as we will recalculate it. 839 elif filename == "META/vbmeta_digest.txt": 840 pass 841 842 # Skip the care_map as we will regenerate the system/vendor images. 843 elif filename in ["META/care_map.pb", "META/care_map.txt"]: 844 pass 845 846 # Skip apex_info.pb because we sign/modify apexes 847 elif filename == "META/apex_info.pb": 848 pass 849 850 # Updates system_other.avbpubkey in /product/etc/. 851 elif filename in ( 852 "PRODUCT/etc/security/avb/system_other.avbpubkey", 853 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"): 854 # Only update system_other's public key, if the corresponding signing 855 # key is specified via --avb_system_other_key. 856 signing_key = OPTIONS.avb_keys.get("system_other") 857 if signing_key: 858 public_key = common.ExtractAvbPublicKey( 859 misc_info['avb_avbtool'], signing_key) 860 print(" Rewriting AVB public key of system_other in /product") 861 common.ZipWrite(output_tf_zip, public_key, filename) 862 863 # Updates pvmfw embedded public key with the virt APEX payload key. 864 elif filename == "PREBUILT_IMAGES/pvmfw.img": 865 # Find the name of the virt APEX in the target files. 866 namelist = input_tf_zip.namelist() 867 apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f)) 868 virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$") 869 virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None) 870 if not virt_apex: 871 print("Removing %s from ramdisk: virt APEX not found" % filename) 872 else: 873 print("Replacing %s embedded key with %s key" % (filename, virt_apex)) 874 # Get the current and new embedded keys. 875 payload_key, container_key, sign_tool = apex_keys[virt_apex] 876 new_pubkey_path = common.ExtractAvbPublicKey( 877 misc_info['avb_avbtool'], payload_key) 878 with open(new_pubkey_path, 'rb') as f: 879 new_pubkey = f.read() 880 pubkey_info = copy.copy( 881 input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey")) 882 old_pubkey = input_tf_zip.read(pubkey_info.filename) 883 # Validate the keys and image. 884 if len(old_pubkey) != len(new_pubkey): 885 raise common.ExternalError("pvmfw embedded public key size mismatch") 886 pos = data.find(old_pubkey) 887 if pos == -1: 888 raise common.ExternalError("pvmfw embedded public key not found") 889 # Replace the key and copy new files. 890 new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):] 891 common.ZipWriteStr(output_tf_zip, out_info, new_data) 892 common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey) 893 elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey": 894 pass 895 896 # Should NOT sign boot-debug.img. 897 elif filename in ( 898 "BOOT/RAMDISK/force_debuggable", 899 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"): 900 raise common.ExternalError("debuggable boot.img cannot be signed") 901 902 # Should NOT sign userdebug sepolicy file. 903 elif filename in ( 904 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil", 905 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"): 906 if not OPTIONS.allow_gsi_debug_sepolicy: 907 raise common.ExternalError("debug sepolicy shouldn't be included") 908 else: 909 # Copy it verbatim if we allow the file to exist. 910 common.ZipWriteStr(output_tf_zip, out_info, data) 911 912 # Sign microdroid_vendor.img. 913 elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img": 914 vendor_key = OPTIONS.avb_keys.get("vendor") 915 vendor_algorithm = OPTIONS.avb_algorithms.get("vendor") 916 with tempfile.NamedTemporaryFile() as image: 917 image.write(data) 918 image.flush() 919 ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm, 920 misc_info) 921 common.ZipWrite(output_tf_zip, image.name, filename) 922 # A non-APK file; copy it verbatim. 923 else: 924 try: 925 entry = output_tf_zip.getinfo(filename) 926 if output_tf_zip.read(entry) != data: 927 logger.warn( 928 "Output zip contains duplicate entries for %s with different contents", filename) 929 continue 930 except KeyError: 931 common.ZipWriteStr(output_tf_zip, out_info, data) 932 933 if OPTIONS.replace_ota_keys: 934 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) 935 936 937 # Write back misc_info with the latest values. 938 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info) 939 940# Parse string output of `avbtool info_image`. 941def ParseAvbInfo(info_raw): 942 # line_matcher is for parsing each output line of `avbtool info_image`. 943 # example string input: " Hash Algorithm: sha1" 944 # example matched input: (" ", "Hash Algorithm", "sha1") 945 line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$') 946 # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`. 947 # example string input: "example_prop_key -> 'example_prop_value'" 948 # example matched output: ("example_prop_key", "example_prop_value") 949 prop_matcher = re.compile(r"(.+)\s->\s'(.+)'") 950 info = {} 951 indent_stack = [[-1, info]] 952 for line_info_raw in info_raw.split('\n'): 953 # Parse the line 954 line_info_parsed = line_matcher.match(line_info_raw) 955 if not line_info_parsed: 956 continue 957 indent = len(line_info_parsed.group(1)) 958 key = line_info_parsed.group(2).strip() 959 value = line_info_parsed.group(3).strip() 960 961 # Pop indentation stack 962 while indent <= indent_stack[-1][0]: 963 del indent_stack[-1] 964 965 # Insert information into 'info'. 966 cur_info = indent_stack[-1][1] 967 if value == "": 968 if key == "Descriptors": 969 empty_list = [] 970 cur_info[key] = empty_list 971 indent_stack.append([indent, empty_list]) 972 else: 973 empty_dict = {} 974 cur_info.append({key:empty_dict}) 975 indent_stack.append([indent, empty_dict]) 976 elif key == "Prop": 977 prop_parsed = prop_matcher.match(value) 978 if not prop_parsed: 979 raise ValueError( 980 "Failed to parse prop while getting avb information.") 981 cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}}) 982 else: 983 cur_info[key] = value 984 return info 985 986def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info): 987 # Get avb information about the image by parsing avbtool info_image. 988 def GetAvbInfo(avbtool, image_name): 989 # Get information with raw string by `avbtool info_image`. 990 info_raw = common.RunAndCheckOutput([ 991 avbtool, 'info_image', 992 '--image', image_name 993 ]) 994 return ParseAvbInfo(info_raw) 995 996 # Get hashtree descriptor from info 997 def GetAvbHashtreeDescriptor(avb_info): 998 hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x, 999 info.get('Descriptors'))) 1000 if len(hashtree_descriptors) != 1: 1001 raise ValueError("The number of hashtree descriptor is not 1.") 1002 return hashtree_descriptors[0]["Hashtree descriptor"] 1003 1004 # Get avb info 1005 avbtool = misc_info['avb_avbtool'] 1006 info = GetAvbInfo(avbtool, image.name) 1007 hashtree_descriptor = GetAvbHashtreeDescriptor(info) 1008 1009 # Generate command 1010 cmd = [avbtool, 'add_hashtree_footer', 1011 '--key', new_key, 1012 '--algorithm', new_algorithm, 1013 '--partition_name', hashtree_descriptor.get("Partition Name"), 1014 '--partition_size', info.get("Image size").removesuffix(" bytes"), 1015 '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"), 1016 '--salt', hashtree_descriptor.get("Salt"), 1017 '--do_not_generate_fec', 1018 '--image', image.name 1019 ] 1020 1021 # Append properties into command 1022 props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x, 1023 info.get('Descriptors'))) 1024 for prop_wrapped in props: 1025 prop = tuple(prop_wrapped.items()) 1026 if len(prop) != 1: 1027 raise ValueError("The number of property is not 1.") 1028 cmd.append('--prop') 1029 cmd.append(prop[0][0] + ':' + prop[0][1]) 1030 1031 # Replace Hashtree Footer with new key 1032 common.RunAndCheckOutput(cmd) 1033 1034 # Check root digest is not changed 1035 new_info = GetAvbInfo(avbtool, image.name) 1036 new_hashtree_descriptor = GetAvbHashtreeDescriptor(info) 1037 root_digest = hashtree_descriptor.get("Root Digest") 1038 new_root_digest = new_hashtree_descriptor.get("Root Digest") 1039 assert root_digest == new_root_digest, \ 1040 ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: " 1041 "{}").format(root_digest, new_root_digest) 1042 1043def ReplaceCerts(data): 1044 """Replaces all the occurences of X.509 certs with the new ones. 1045 1046 The mapping info is read from OPTIONS.key_map. Non-existent certificate will 1047 be skipped. After the replacement, it additionally checks for duplicate 1048 entries, which would otherwise fail the policy loading code in 1049 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java. 1050 1051 Args: 1052 data: Input string that contains a set of X.509 certs. 1053 1054 Returns: 1055 A string after the replacement. 1056 1057 Raises: 1058 AssertionError: On finding duplicate entries. 1059 """ 1060 for old, new in OPTIONS.key_map.items(): 1061 if OPTIONS.verbose: 1062 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new)) 1063 1064 try: 1065 with open(old + ".x509.pem") as old_fp: 1066 old_cert16 = base64.b16encode( 1067 common.ParseCertificate(old_fp.read())).decode().lower() 1068 with open(new + ".x509.pem") as new_fp: 1069 new_cert16 = base64.b16encode( 1070 common.ParseCertificate(new_fp.read())).decode().lower() 1071 except IOError as e: 1072 if OPTIONS.verbose or e.errno != errno.ENOENT: 1073 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with " 1074 "%s.x509.pem." % (e.filename, e.strerror, old, new)) 1075 continue 1076 1077 # Only match entire certs. 1078 pattern = "\\b" + old_cert16 + "\\b" 1079 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) 1080 1081 if OPTIONS.verbose: 1082 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % ( 1083 num, old, new)) 1084 1085 # Verify that there're no duplicate entries after the replacement. Note that 1086 # it's only checking entries with global seinfo at the moment (i.e. ignoring 1087 # the ones with inner packages). (Bug: 69479366) 1088 root = ElementTree.fromstring(data) 1089 signatures = [signer.attrib['signature'] 1090 for signer in root.findall('signer')] 1091 assert len(signatures) == len(set(signatures)), \ 1092 "Found duplicate entries after cert replacement: {}".format(data) 1093 1094 return data 1095 1096 1097def EditTags(tags): 1098 """Applies the edits to the tag string as specified in OPTIONS.tag_changes. 1099 1100 Args: 1101 tags: The input string that contains comma-separated tags. 1102 1103 Returns: 1104 The updated tags (comma-separated and sorted). 1105 """ 1106 tags = set(tags.split(",")) 1107 for ch in OPTIONS.tag_changes: 1108 if ch[0] == "-": 1109 tags.discard(ch[1:]) 1110 elif ch[0] == "+": 1111 tags.add(ch[1:]) 1112 return ",".join(sorted(tags)) 1113 1114 1115def RewriteProps(data): 1116 """Rewrites the system properties in the given string. 1117 1118 Each property is expected in 'key=value' format. The properties that contain 1119 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling 1120 EditTags(). 1121 1122 Args: 1123 data: Input string, separated by newlines. 1124 1125 Returns: 1126 The string with modified properties. 1127 """ 1128 output = [] 1129 for line in data.split("\n"): 1130 line = line.strip() 1131 original_line = line 1132 if line and line[0] != '#' and "=" in line: 1133 key, value = line.split("=", 1) 1134 if (key.startswith("ro.") and 1135 key.endswith((".build.fingerprint", ".build.thumbprint"))): 1136 pieces = value.split("/") 1137 pieces[-1] = EditTags(pieces[-1]) 1138 value = "/".join(pieces) 1139 elif key == "ro.bootimage.build.fingerprint": 1140 pieces = value.split("/") 1141 pieces[-1] = EditTags(pieces[-1]) 1142 value = "/".join(pieces) 1143 elif key == "ro.build.description": 1144 pieces = value.split() 1145 assert pieces[-1].endswith("-keys") 1146 pieces[-1] = EditTags(pieces[-1]) 1147 value = " ".join(pieces) 1148 elif key.startswith("ro.") and key.endswith(".build.tags"): 1149 value = EditTags(value) 1150 elif key == "ro.build.display.id": 1151 # change, eg, "JWR66N dev-keys" to "JWR66N" 1152 value = value.split() 1153 if len(value) > 1 and value[-1].endswith("-keys"): 1154 value.pop() 1155 value = " ".join(value) 1156 line = key + "=" + value 1157 if line != original_line: 1158 print(" replace: ", original_line) 1159 print(" with: ", line) 1160 output.append(line) 1161 return "\n".join(output) + "\n" 1162 1163 1164def WriteOtacerts(output_zip, filename, keys): 1165 """Constructs a zipfile from given keys; and writes it to output_zip. 1166 1167 Args: 1168 output_zip: The output target_files zip. 1169 filename: The archive name in the output zip. 1170 keys: A list of public keys to use during OTA package verification. 1171 """ 1172 temp_file = io.BytesIO() 1173 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True) 1174 for k in keys: 1175 common.ZipWrite(certs_zip, k) 1176 common.ZipClose(certs_zip) 1177 common.ZipWriteStr(output_zip, filename, temp_file.getvalue()) 1178 1179 1180def ReplaceOtaKeys(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info): 1181 try: 1182 keylist = input_tf_zip.read("META/otakeys.txt").decode().split() 1183 except KeyError: 1184 raise common.ExternalError("can't read META/otakeys.txt from input") 1185 1186 extra_ota_keys_info = misc_info.get("extra_ota_keys") 1187 if extra_ota_keys_info: 1188 extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" 1189 for k in extra_ota_keys_info.split()] 1190 print("extra ota key(s): " + ", ".join(extra_ota_keys)) 1191 else: 1192 extra_ota_keys = [] 1193 for k in extra_ota_keys: 1194 if not os.path.isfile(k): 1195 raise common.ExternalError(k + " does not exist or is not a file") 1196 1197 extra_recovery_keys_info = misc_info.get("extra_recovery_keys") 1198 if extra_recovery_keys_info: 1199 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" 1200 for k in extra_recovery_keys_info.split()] 1201 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) 1202 else: 1203 extra_recovery_keys = [] 1204 for k in extra_recovery_keys: 1205 if not os.path.isfile(k): 1206 raise common.ExternalError(k + " does not exist or is not a file") 1207 1208 mapped_keys = [] 1209 for k in keylist: 1210 m = re.match(r"^(.*)\.x509\.pem$", k) 1211 if not m: 1212 raise common.ExternalError( 1213 "can't parse \"%s\" from META/otakeys.txt" % (k,)) 1214 k = m.group(1) 1215 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") 1216 1217 if mapped_keys: 1218 print("using:\n ", "\n ".join(mapped_keys)) 1219 print("for OTA package verification") 1220 else: 1221 devkey = misc_info.get("default_system_dev_certificate", 1222 "build/make/target/product/security/testkey") 1223 mapped_devkey = OPTIONS.key_map.get(devkey, devkey) 1224 if mapped_devkey != devkey: 1225 misc_info["default_system_dev_certificate"] = mapped_devkey 1226 mapped_keys.append(mapped_devkey + ".x509.pem") 1227 print("META/otakeys.txt has no keys; using %s for OTA package" 1228 " verification." % (mapped_keys[0],)) 1229 for k in mapped_keys: 1230 if not os.path.isfile(k): 1231 raise common.ExternalError(k + " does not exist or is not a file") 1232 1233 otacerts = [info 1234 for info in input_tf_zip.infolist() 1235 if info.filename.endswith("/otacerts.zip")] 1236 for info in otacerts: 1237 if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")): 1238 extra_keys = extra_recovery_keys 1239 else: 1240 extra_keys = extra_ota_keys 1241 print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys) 1242 WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys) 1243 1244 1245def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info): 1246 """Replaces META/misc_info.txt. 1247 1248 Only writes back the ones in the original META/misc_info.txt. Because the 1249 current in-memory dict contains additional items computed at runtime. 1250 """ 1251 misc_info_old = common.LoadDictionaryFromLines( 1252 input_zip.read('META/misc_info.txt').decode().split('\n')) 1253 items = [] 1254 for key in sorted(misc_info): 1255 if key in misc_info_old: 1256 items.append('%s=%s' % (key, misc_info[key])) 1257 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items)) 1258 1259 1260def ReplaceAvbSigningKeys(misc_info): 1261 """Replaces the AVB signing keys.""" 1262 1263 def ReplaceAvbPartitionSigningKey(partition): 1264 key = OPTIONS.avb_keys.get(partition) 1265 if not key: 1266 return 1267 1268 algorithm = OPTIONS.avb_algorithms.get(partition) 1269 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,) 1270 1271 print('Replacing AVB signing key for %s with "%s" (%s)' % ( 1272 partition, key, algorithm)) 1273 misc_info['avb_' + partition + '_algorithm'] = algorithm 1274 misc_info['avb_' + partition + '_key_path'] = key 1275 1276 extra_args = OPTIONS.avb_extra_args.get(partition) 1277 if extra_args: 1278 print('Setting extra AVB signing args for %s to "%s"' % ( 1279 partition, extra_args)) 1280 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get( 1281 partition, 1282 # custom partition 1283 "avb_{}_add_hashtree_footer_args".format(partition)) 1284 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args) 1285 1286 for partition in AVB_FOOTER_ARGS_BY_PARTITION: 1287 ReplaceAvbPartitionSigningKey(partition) 1288 1289 for custom_partition in misc_info.get( 1290 "avb_custom_images_partition_list", "").strip().split(): 1291 ReplaceAvbPartitionSigningKey(custom_partition) 1292 1293 1294def RewriteAvbProps(misc_info): 1295 """Rewrites the props in AVB signing args.""" 1296 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items(): 1297 args = misc_info.get(args_key) 1298 if not args: 1299 continue 1300 1301 tokens = [] 1302 changed = False 1303 for token in args.split(): 1304 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition) 1305 if not token.startswith(fingerprint_key): 1306 tokens.append(token) 1307 continue 1308 prefix, tag = token.rsplit('/', 1) 1309 tokens.append('{}/{}'.format(prefix, EditTags(tag))) 1310 changed = True 1311 1312 if changed: 1313 result = ' '.join(tokens) 1314 print('Rewriting AVB prop for {}:\n'.format(partition)) 1315 print(' replace: {}'.format(args)) 1316 print(' with: {}'.format(result)) 1317 misc_info[args_key] = result 1318 1319 1320def BuildKeyMap(misc_info, key_mapping_options): 1321 for s, d in key_mapping_options: 1322 if s is None: # -d option 1323 devkey = misc_info.get("default_system_dev_certificate", 1324 "build/make/target/product/security/testkey") 1325 devkeydir = os.path.dirname(devkey) 1326 1327 OPTIONS.key_map.update({ 1328 devkeydir + "/testkey": d + "/releasekey", 1329 devkeydir + "/devkey": d + "/releasekey", 1330 devkeydir + "/media": d + "/media", 1331 devkeydir + "/shared": d + "/shared", 1332 devkeydir + "/platform": d + "/platform", 1333 devkeydir + "/networkstack": d + "/networkstack", 1334 devkeydir + "/sdk_sandbox": d + "/sdk_sandbox", 1335 }) 1336 else: 1337 OPTIONS.key_map[s] = d 1338 1339 1340def GetApiLevelAndCodename(input_tf_zip): 1341 data = input_tf_zip.read("SYSTEM/build.prop").decode() 1342 api_level = None 1343 codename = None 1344 for line in data.split("\n"): 1345 line = line.strip() 1346 if line and line[0] != '#' and "=" in line: 1347 key, value = line.split("=", 1) 1348 key = key.strip() 1349 if key == "ro.build.version.sdk": 1350 api_level = int(value.strip()) 1351 elif key == "ro.build.version.codename": 1352 codename = value.strip() 1353 1354 if api_level is None: 1355 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") 1356 if codename is None: 1357 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop") 1358 1359 return (api_level, codename) 1360 1361 1362def GetCodenameToApiLevelMap(input_tf_zip): 1363 data = input_tf_zip.read("SYSTEM/build.prop").decode() 1364 api_level = None 1365 codenames = None 1366 for line in data.split("\n"): 1367 line = line.strip() 1368 if line and line[0] != '#' and "=" in line: 1369 key, value = line.split("=", 1) 1370 key = key.strip() 1371 if key == "ro.build.version.sdk": 1372 api_level = int(value.strip()) 1373 elif key == "ro.build.version.all_codenames": 1374 codenames = value.strip().split(",") 1375 1376 if api_level is None: 1377 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") 1378 if codenames is None: 1379 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop") 1380 1381 result = {} 1382 for codename in codenames: 1383 codename = codename.strip() 1384 if codename: 1385 result[codename] = api_level 1386 return result 1387 1388 1389def ReadApexKeysInfo(tf_zip): 1390 """Parses the APEX keys info from a given target-files zip. 1391 1392 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a 1393 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a 1394 tuple of (payload_key, container_key, sign_tool). 1395 1396 Args: 1397 tf_zip: The input target_files ZipFile (already open). 1398 1399 Returns: 1400 (payload_key, container_key, sign_tool): 1401 - payload_key contains the path to the payload signing key 1402 - container_key contains the path to the container signing key 1403 - sign_tool is an apex-specific signing tool for its payload contents 1404 """ 1405 keys = {} 1406 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'): 1407 line = line.strip() 1408 if not line: 1409 continue 1410 matches = re.match( 1411 r'^name="(?P<NAME>.*)"\s+' 1412 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+' 1413 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+' 1414 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+' 1415 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"' 1416 r'(\s+partition="(?P<PARTITION>.*?)")?' 1417 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$', 1418 line) 1419 if not matches: 1420 continue 1421 1422 name = matches.group('NAME') 1423 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY") 1424 1425 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix): 1426 pubkey_suffix_len = len(pubkey_suffix) 1427 privkey_suffix_len = len(privkey_suffix) 1428 return (pubkey.endswith(pubkey_suffix) and 1429 privkey.endswith(privkey_suffix) and 1430 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len]) 1431 1432 # Check the container key names, as we'll carry them without the 1433 # extensions. This doesn't apply to payload keys though, which we will use 1434 # full names only. 1435 container_cert = matches.group("CONTAINER_CERT") 1436 container_private_key = matches.group("CONTAINER_PRIVATE_KEY") 1437 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED': 1438 container_key = 'PRESIGNED' 1439 elif CompareKeys( 1440 container_cert, OPTIONS.public_key_suffix, 1441 container_private_key, OPTIONS.private_key_suffix): 1442 container_key = container_cert[:-len(OPTIONS.public_key_suffix)] 1443 else: 1444 raise ValueError("Failed to parse container keys: \n{}".format(line)) 1445 1446 sign_tool = matches.group("SIGN_TOOL") 1447 keys[name] = (payload_private_key, container_key, sign_tool) 1448 1449 return keys 1450 1451 1452def BuildVendorPartitions(output_zip_path): 1453 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools.""" 1454 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS): 1455 logger.warning("Allowed --vendor_partitions: %s", 1456 ",".join(ALLOWED_VENDOR_PARTITIONS)) 1457 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection( 1458 OPTIONS.vendor_partitions) 1459 1460 logger.info("Building vendor partitions using vendor otatools.") 1461 vendor_tempdir = common.UnzipTemp(output_zip_path, [ 1462 "META/*", 1463 "SYSTEM/build.prop", 1464 "RECOVERY/*", 1465 "BOOT/*", 1466 "OTA/", 1467 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions]) 1468 1469 # Disable various partitions that build based on misc_info fields. 1470 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using 1471 # vendor otatools. These other partitions will be rebuilt using the main 1472 # otatools if necessary. 1473 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt") 1474 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path) 1475 # Ignore if not rebuilding recovery 1476 if not OPTIONS.rebuild_recovery: 1477 vendor_misc_info["no_boot"] = "true" # boot 1478 vendor_misc_info["vendor_boot"] = "false" # vendor_boot 1479 vendor_misc_info["no_recovery"] = "true" # recovery 1480 vendor_misc_info["avb_enable"] = "false" # vbmeta 1481 1482 vendor_misc_info["has_dtbo"] = "false" # dtbo 1483 vendor_misc_info["has_pvmfw"] = "false" # pvmfw 1484 vendor_misc_info["avb_custom_images_partition_list"] = "" # avb custom images 1485 vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta 1486 vendor_misc_info["custom_images_partition_list"] = "" # custom images 1487 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty 1488 vendor_misc_info["build_super_partition"] = "false" # super split 1489 vendor_misc_info["avb_vbmeta_system"] = "" # skip building vbmeta_system 1490 with open(vendor_misc_info_path, "w") as output: 1491 for key in sorted(vendor_misc_info): 1492 output.write("{}={}\n".format(key, vendor_misc_info[key])) 1493 1494 # Disable system partition by a placeholder of IMAGES/system.img, 1495 # instead of removing SYSTEM folder. 1496 # Because SYSTEM/build.prop is still needed for: 1497 # add_img_to_target_files.CreateImage -> 1498 # common.BuildInfo -> 1499 # common.BuildInfo.CalculateFingerprint 1500 vendor_images_path = os.path.join(vendor_tempdir, "IMAGES") 1501 if not os.path.exists(vendor_images_path): 1502 os.makedirs(vendor_images_path) 1503 with open(os.path.join(vendor_images_path, "system.img"), "w") as output: 1504 pass 1505 1506 # Disable care_map.pb as not all ab_partitions are available when 1507 # vendor otatools regenerates vendor images. 1508 if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")): 1509 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt")) 1510 # Disable RADIO images 1511 if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")): 1512 os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")) 1513 1514 # Build vendor images using vendor otatools. 1515 # Accept either a zip file or extracted directory. 1516 if os.path.isfile(OPTIONS.vendor_otatools): 1517 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_") 1518 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir) 1519 else: 1520 vendor_otatools_dir = OPTIONS.vendor_otatools 1521 cmd = [ 1522 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"), 1523 "--is_signing", 1524 "--add_missing", 1525 "--verbose", 1526 vendor_tempdir, 1527 ] 1528 if OPTIONS.rebuild_recovery: 1529 cmd.insert(4, "--rebuild_recovery") 1530 1531 common.RunAndCheckOutput(cmd, verbose=True) 1532 1533 logger.info("Writing vendor partitions to output archive.") 1534 with zipfile.ZipFile( 1535 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED, 1536 allowZip64=True) as output_zip: 1537 for p in OPTIONS.vendor_partitions: 1538 img_file_path = "IMAGES/{}.img".format(p) 1539 map_file_path = "IMAGES/{}.map".format(p) 1540 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path) 1541 if os.path.exists(os.path.join(vendor_tempdir, map_file_path)): 1542 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path) 1543 # copy recovery.img, boot.img, recovery patch & install.sh 1544 if OPTIONS.rebuild_recovery: 1545 recovery_img = "IMAGES/recovery.img" 1546 boot_img = "IMAGES/boot.img" 1547 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img) 1548 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img) 1549 recovery_patch_path = "VENDOR/recovery-from-boot.p" 1550 recovery_sh_path = "VENDOR/bin/install-recovery.sh" 1551 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path) 1552 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path) 1553 1554 1555def main(argv): 1556 1557 key_mapping_options = [] 1558 1559 def option_handler(o, a): 1560 if o in ("-e", "--extra_apks"): 1561 names, key = a.split("=") 1562 names = names.split(",") 1563 for n in names: 1564 OPTIONS.extra_apks[n] = key 1565 elif o == "--extra_apex_payload_key": 1566 apex_names, key = a.split("=") 1567 for name in apex_names.split(","): 1568 OPTIONS.extra_apex_payload_keys[name] = key 1569 elif o == "--skip_apks_with_path_prefix": 1570 # Check the prefix, which must be in all upper case. 1571 prefix = a.split('/')[0] 1572 if not prefix or prefix != prefix.upper(): 1573 raise ValueError("Invalid path prefix '%s'" % (a,)) 1574 OPTIONS.skip_apks_with_path_prefix.add(a) 1575 elif o in ("-d", "--default_key_mappings"): 1576 key_mapping_options.append((None, a)) 1577 elif o in ("-k", "--key_mapping"): 1578 key_mapping_options.append(a.split("=", 1)) 1579 elif o in ("-o", "--replace_ota_keys"): 1580 OPTIONS.replace_ota_keys = True 1581 elif o in ("-t", "--tag_changes"): 1582 new = [] 1583 for i in a.split(","): 1584 i = i.strip() 1585 if not i or i[0] not in "-+": 1586 raise ValueError("Bad tag change '%s'" % (i,)) 1587 new.append(i[0] + i[1:].strip()) 1588 OPTIONS.tag_changes = tuple(new) 1589 elif o == "--replace_verity_public_key": 1590 raise ValueError("--replace_verity_public_key is no longer supported," 1591 " please switch to AVB") 1592 elif o == "--replace_verity_private_key": 1593 raise ValueError("--replace_verity_private_key is no longer supported," 1594 " please switch to AVB") 1595 elif o == "--replace_verity_keyid": 1596 raise ValueError("--replace_verity_keyid is no longer supported, please" 1597 " switch to AVB") 1598 elif o == "--remove_avb_public_keys": 1599 OPTIONS.remove_avb_public_keys = a.split(",") 1600 elif o == "--avb_vbmeta_key": 1601 OPTIONS.avb_keys['vbmeta'] = a 1602 elif o == "--avb_vbmeta_algorithm": 1603 OPTIONS.avb_algorithms['vbmeta'] = a 1604 elif o == "--avb_vbmeta_extra_args": 1605 OPTIONS.avb_extra_args['vbmeta'] = a 1606 elif o == "--avb_boot_key": 1607 OPTIONS.avb_keys['boot'] = a 1608 elif o == "--avb_boot_algorithm": 1609 OPTIONS.avb_algorithms['boot'] = a 1610 elif o == "--avb_boot_extra_args": 1611 OPTIONS.avb_extra_args['boot'] = a 1612 elif o == "--avb_dtbo_key": 1613 OPTIONS.avb_keys['dtbo'] = a 1614 elif o == "--avb_dtbo_algorithm": 1615 OPTIONS.avb_algorithms['dtbo'] = a 1616 elif o == "--avb_dtbo_extra_args": 1617 OPTIONS.avb_extra_args['dtbo'] = a 1618 elif o == "--avb_init_boot_key": 1619 OPTIONS.avb_keys['init_boot'] = a 1620 elif o == "--avb_init_boot_algorithm": 1621 OPTIONS.avb_algorithms['init_boot'] = a 1622 elif o == "--avb_init_boot_extra_args": 1623 OPTIONS.avb_extra_args['init_boot'] = a 1624 elif o == "--avb_recovery_key": 1625 OPTIONS.avb_keys['recovery'] = a 1626 elif o == "--avb_recovery_algorithm": 1627 OPTIONS.avb_algorithms['recovery'] = a 1628 elif o == "--avb_recovery_extra_args": 1629 OPTIONS.avb_extra_args['recovery'] = a 1630 elif o == "--avb_system_key": 1631 OPTIONS.avb_keys['system'] = a 1632 elif o == "--avb_system_algorithm": 1633 OPTIONS.avb_algorithms['system'] = a 1634 elif o == "--avb_system_extra_args": 1635 OPTIONS.avb_extra_args['system'] = a 1636 elif o == "--avb_system_other_key": 1637 OPTIONS.avb_keys['system_other'] = a 1638 elif o == "--avb_system_other_algorithm": 1639 OPTIONS.avb_algorithms['system_other'] = a 1640 elif o == "--avb_system_other_extra_args": 1641 OPTIONS.avb_extra_args['system_other'] = a 1642 elif o == "--avb_vendor_key": 1643 OPTIONS.avb_keys['vendor'] = a 1644 elif o == "--avb_vendor_algorithm": 1645 OPTIONS.avb_algorithms['vendor'] = a 1646 elif o == "--avb_vendor_extra_args": 1647 OPTIONS.avb_extra_args['vendor'] = a 1648 elif o == "--avb_vbmeta_system_key": 1649 OPTIONS.avb_keys['vbmeta_system'] = a 1650 elif o == "--avb_vbmeta_system_algorithm": 1651 OPTIONS.avb_algorithms['vbmeta_system'] = a 1652 elif o == "--avb_vbmeta_system_extra_args": 1653 OPTIONS.avb_extra_args['vbmeta_system'] = a 1654 elif o == "--avb_vbmeta_vendor_key": 1655 OPTIONS.avb_keys['vbmeta_vendor'] = a 1656 elif o == "--avb_vbmeta_vendor_algorithm": 1657 OPTIONS.avb_algorithms['vbmeta_vendor'] = a 1658 elif o == "--avb_vbmeta_vendor_extra_args": 1659 OPTIONS.avb_extra_args['vbmeta_vendor'] = a 1660 elif o == "--avb_apex_extra_args": 1661 OPTIONS.avb_extra_args['apex'] = a 1662 elif o == "--avb_extra_custom_image_key": 1663 partition, key = a.split("=") 1664 OPTIONS.avb_keys[partition] = key 1665 elif o == "--avb_extra_custom_image_algorithm": 1666 partition, algorithm = a.split("=") 1667 OPTIONS.avb_algorithms[partition] = algorithm 1668 elif o == "--avb_extra_custom_image_extra_args": 1669 # Setting the maxsplit parameter to one, which will return a list with 1670 # two elements. e.g., the second '=' should not be splitted for 1671 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'. 1672 partition, extra_args = a.split("=", 1) 1673 OPTIONS.avb_extra_args[partition] = extra_args 1674 elif o == "--vendor_otatools": 1675 OPTIONS.vendor_otatools = a 1676 elif o == "--vendor_partitions": 1677 OPTIONS.vendor_partitions = set(a.split(",")) 1678 elif o == "--allow_gsi_debug_sepolicy": 1679 OPTIONS.allow_gsi_debug_sepolicy = True 1680 elif o == "--override_apk_keys": 1681 OPTIONS.override_apk_keys = a 1682 elif o == "--override_apex_keys": 1683 OPTIONS.override_apex_keys = a 1684 elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"): 1685 print(f"{o} is deprecated and does nothing") 1686 else: 1687 return False 1688 return True 1689 1690 args = common.ParseOptions( 1691 argv, __doc__, 1692 extra_opts="e:d:k:ot:", 1693 extra_long_opts=[ 1694 "extra_apks=", 1695 "extra_apex_payload_key=", 1696 "skip_apks_with_path_prefix=", 1697 "default_key_mappings=", 1698 "key_mapping=", 1699 "replace_ota_keys", 1700 "tag_changes=", 1701 "replace_verity_public_key=", 1702 "replace_verity_private_key=", 1703 "replace_verity_keyid=", 1704 "remove_avb_public_keys=", 1705 "avb_apex_extra_args=", 1706 "avb_vbmeta_algorithm=", 1707 "avb_vbmeta_key=", 1708 "avb_vbmeta_extra_args=", 1709 "avb_boot_algorithm=", 1710 "avb_boot_key=", 1711 "avb_boot_extra_args=", 1712 "avb_dtbo_algorithm=", 1713 "avb_dtbo_key=", 1714 "avb_dtbo_extra_args=", 1715 "avb_init_boot_algorithm=", 1716 "avb_init_boot_key=", 1717 "avb_init_boot_extra_args=", 1718 "avb_recovery_algorithm=", 1719 "avb_recovery_key=", 1720 "avb_recovery_extra_args=", 1721 "avb_system_algorithm=", 1722 "avb_system_key=", 1723 "avb_system_extra_args=", 1724 "avb_system_other_algorithm=", 1725 "avb_system_other_key=", 1726 "avb_system_other_extra_args=", 1727 "avb_vendor_algorithm=", 1728 "avb_vendor_key=", 1729 "avb_vendor_extra_args=", 1730 "avb_vbmeta_system_algorithm=", 1731 "avb_vbmeta_system_key=", 1732 "avb_vbmeta_system_extra_args=", 1733 "avb_vbmeta_vendor_algorithm=", 1734 "avb_vbmeta_vendor_key=", 1735 "avb_vbmeta_vendor_extra_args=", 1736 "avb_extra_custom_image_key=", 1737 "avb_extra_custom_image_algorithm=", 1738 "avb_extra_custom_image_extra_args=", 1739 "gki_signing_key=", 1740 "gki_signing_algorithm=", 1741 "gki_signing_extra_args=", 1742 "vendor_partitions=", 1743 "vendor_otatools=", 1744 "allow_gsi_debug_sepolicy", 1745 "override_apk_keys=", 1746 "override_apex_keys=", 1747 ], 1748 extra_option_handler=[option_handler, payload_signer.signer_options]) 1749 1750 if len(args) != 2: 1751 common.Usage(__doc__) 1752 sys.exit(1) 1753 1754 common.InitLogging() 1755 1756 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True) 1757 output_zip = zipfile.ZipFile(args[1], "w", 1758 compression=zipfile.ZIP_DEFLATED, 1759 allowZip64=True) 1760 1761 misc_info = common.LoadInfoDict(input_zip) 1762 if OPTIONS.package_key is None: 1763 OPTIONS.package_key = misc_info.get( 1764 "default_system_dev_certificate", 1765 "build/make/target/product/security/testkey") 1766 1767 BuildKeyMap(misc_info, key_mapping_options) 1768 1769 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip) 1770 apk_keys = GetApkCerts(apk_keys_info) 1771 1772 apex_keys_info = ReadApexKeysInfo(input_zip) 1773 apex_keys = GetApexKeys(apex_keys_info, apk_keys) 1774 1775 # TODO(xunchang) check for the apks inside the apex files, and abort early if 1776 # the keys are not available. 1777 CheckApkAndApexKeysAvailable( 1778 input_zip, 1779 set(apk_keys.keys()) | set(apex_keys.keys()), 1780 compressed_extension, 1781 apex_keys) 1782 1783 key_passwords = common.GetKeyPasswords( 1784 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values()))) 1785 platform_api_level, _ = GetApiLevelAndCodename(input_zip) 1786 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip) 1787 1788 ProcessTargetFiles(input_zip, output_zip, misc_info, 1789 apk_keys, apex_keys, key_passwords, 1790 platform_api_level, codename_to_api_level_map, 1791 compressed_extension) 1792 1793 common.ZipClose(input_zip) 1794 common.ZipClose(output_zip) 1795 1796 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools: 1797 BuildVendorPartitions(args[1]) 1798 1799 # Skip building userdata.img and cache.img when signing the target files. 1800 new_args = ["--is_signing", "--add_missing", "--verbose"] 1801 # add_img_to_target_files builds the system image from scratch, so the 1802 # recovery patch is guaranteed to be regenerated there. 1803 if OPTIONS.rebuild_recovery: 1804 new_args.append("--rebuild_recovery") 1805 new_args.append(args[1]) 1806 add_img_to_target_files.main(new_args) 1807 1808 print("done.") 1809 1810 1811if __name__ == '__main__': 1812 try: 1813 main(sys.argv[1:]) 1814 finally: 1815 common.Cleanup() 1816