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