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