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