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