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