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