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 name/key pairs as though they appeared in 25 apkcerts.txt (so mappings specified by -k and -d are applied). 26 Keys specified in -e override any value for that app contained 27 in the apkcerts.txt file. Option may be repeated to give 28 multiple extra packages. 29 30 -k (--key_mapping) <src_key=dest_key> 31 Add a mapping from the key name as specified in apkcerts.txt (the 32 src_key) to the real key you wish to sign the package with 33 (dest_key). Option may be repeated to give multiple key 34 mappings. 35 36 -d (--default_key_mappings) <dir> 37 Set up the following key mappings: 38 39 $devkey/devkey ==> $dir/releasekey 40 $devkey/testkey ==> $dir/releasekey 41 $devkey/media ==> $dir/media 42 $devkey/shared ==> $dir/shared 43 $devkey/platform ==> $dir/platform 44 45 where $devkey is the directory part of the value of 46 default_system_dev_certificate from the input target-files's 47 META/misc_info.txt. (Defaulting to "build/target/product/security" 48 if the value is not present in misc_info. 49 50 -d and -k options are added to the set of mappings in the order 51 in which they appear on the command line. 52 53 -o (--replace_ota_keys) 54 Replace the certificate (public key) used by OTA package verification 55 with the ones specified in the input target_files zip (in the 56 META/otakeys.txt file). Key remapping (-k and -d) is performed on the 57 keys. For A/B devices, the payload verification key will be replaced 58 as well. If there're multiple OTA keys, only the first one will be used 59 for payload verification. 60 61 -t (--tag_changes) <+tag>,<-tag>,... 62 Comma-separated list of changes to make to the set of tags (in 63 the last component of the build fingerprint). Prefix each with 64 '+' or '-' to indicate whether that tag should be added or 65 removed. Changes are processed in the order they appear. 66 Default value is "-test-keys,-dev-keys,+release-keys". 67 68 --replace_verity_private_key <key> 69 Replace the private key used for verity signing. It expects a filename 70 WITHOUT the extension (e.g. verity_key). 71 72 --replace_verity_public_key <key> 73 Replace the certificate (public key) used for verity verification. The 74 key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key 75 for devices using system_root_image). It expects the key filename WITH 76 the extension (e.g. verity_key.pub). 77 78 --replace_verity_keyid <path_to_X509_PEM_cert_file> 79 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip 80 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>. 81 82 --avb_{boot,system,vendor,dtbo,vbmeta}_algorithm <algorithm> 83 --avb_{boot,system,vendor,dtbo,vbmeta}_key <key> 84 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign 85 the specified image. Otherwise it uses the existing values in info dict. 86 87 --avb_{boot,system,vendor,dtbo,vbmeta}_extra_args <args> 88 Specify any additional args that are needed to AVB-sign the image 89 (e.g. "--signing_helper /path/to/helper"). The args will be appended to 90 the existing ones in info dict. 91 """ 92 93 import sys 94 95 if sys.hexversion < 0x02070000: 96 print >> sys.stderr, "Python 2.7 or newer is required." 97 sys.exit(1) 98 99 import base64 100 import cStringIO 101 import copy 102 import errno 103 import gzip 104 import os 105 import re 106 import shutil 107 import stat 108 import subprocess 109 import tempfile 110 import zipfile 111 112 import add_img_to_target_files 113 import common 114 115 OPTIONS = common.OPTIONS 116 117 OPTIONS.extra_apks = {} 118 OPTIONS.key_map = {} 119 OPTIONS.rebuild_recovery = False 120 OPTIONS.replace_ota_keys = False 121 OPTIONS.replace_verity_public_key = False 122 OPTIONS.replace_verity_private_key = False 123 OPTIONS.replace_verity_keyid = False 124 OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys") 125 OPTIONS.avb_keys = {} 126 OPTIONS.avb_algorithms = {} 127 OPTIONS.avb_extra_args = {} 128 129 def GetApkCerts(certmap): 130 # apply the key remapping to the contents of the file 131 for apk, cert in certmap.iteritems(): 132 certmap[apk] = OPTIONS.key_map.get(cert, cert) 133 134 # apply all the -e options, overriding anything in the file 135 for apk, cert in OPTIONS.extra_apks.iteritems(): 136 if not cert: 137 cert = "PRESIGNED" 138 certmap[apk] = OPTIONS.key_map.get(cert, cert) 139 140 return certmap 141 142 143 def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension): 144 """Check that all the APKs we want to sign have keys specified, and 145 error out if they don't.""" 146 unknown_apks = [] 147 compressed_apk_extension = None 148 if compressed_extension: 149 compressed_apk_extension = ".apk" + compressed_extension 150 for info in input_tf_zip.infolist(): 151 if (info.filename.endswith(".apk") or 152 (compressed_apk_extension and info.filename.endswith(compressed_apk_extension))): 153 name = os.path.basename(info.filename) 154 if compressed_apk_extension and name.endswith(compressed_apk_extension): 155 name = name[:-len(compressed_extension)] 156 if name not in apk_key_map: 157 unknown_apks.append(name) 158 if unknown_apks: 159 print "ERROR: no key specified for:\n\n ", 160 print "\n ".join(unknown_apks) 161 print "\nUse '-e <apkname>=' to specify a key (which may be an" 162 print "empty string to not sign this apk)." 163 sys.exit(1) 164 165 166 def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, 167 is_compressed): 168 unsigned = tempfile.NamedTemporaryFile() 169 unsigned.write(data) 170 unsigned.flush() 171 172 if is_compressed: 173 uncompressed = tempfile.NamedTemporaryFile() 174 with gzip.open(unsigned.name, "rb") as in_file, open(uncompressed.name, "wb") as out_file: 175 shutil.copyfileobj(in_file, out_file) 176 177 # Finally, close the "unsigned" file (which is gzip compressed), and then 178 # replace it with the uncompressed version. 179 # 180 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use, 181 # we could just gzip / gunzip in-memory buffers instead. 182 unsigned.close() 183 unsigned = uncompressed 184 185 signed = tempfile.NamedTemporaryFile() 186 187 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's 188 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK 189 # didn't change, we don't want its signature to change due to the switch 190 # from SHA-1 to SHA-256. 191 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion 192 # is 18 or higher. For pre-N builds we disable this mechanism by pretending 193 # that the APK's minSdkVersion is 1. 194 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to 195 # determine whether to use SHA-256. 196 min_api_level = None 197 if platform_api_level > 23: 198 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's 199 # minSdkVersion attribute 200 min_api_level = None 201 else: 202 # Force APK signer to use SHA-1 203 min_api_level = 1 204 205 common.SignFile(unsigned.name, signed.name, keyname, pw, 206 min_api_level=min_api_level, 207 codename_to_api_level_map=codename_to_api_level_map) 208 209 data = None; 210 if is_compressed: 211 # Recompress the file after it has been signed. 212 compressed = tempfile.NamedTemporaryFile() 213 with open(signed.name, "rb") as in_file, gzip.open(compressed.name, "wb") as out_file: 214 shutil.copyfileobj(in_file, out_file) 215 216 data = compressed.read() 217 compressed.close() 218 else: 219 data = signed.read() 220 221 unsigned.close() 222 signed.close() 223 224 return data 225 226 227 def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, 228 apk_key_map, key_passwords, platform_api_level, 229 codename_to_api_level_map, 230 compressed_extension): 231 232 compressed_apk_extension = None 233 if compressed_extension: 234 compressed_apk_extension = ".apk" + compressed_extension 235 236 maxsize = max([len(os.path.basename(i.filename)) 237 for i in input_tf_zip.infolist() 238 if i.filename.endswith('.apk') or 239 (compressed_apk_extension and i.filename.endswith(compressed_apk_extension))]) 240 system_root_image = misc_info.get("system_root_image") == "true" 241 242 for info in input_tf_zip.infolist(): 243 if info.filename.startswith("IMAGES/"): 244 continue 245 246 data = input_tf_zip.read(info.filename) 247 out_info = copy.copy(info) 248 249 # Sign APKs. 250 if (info.filename.endswith(".apk") or 251 (compressed_apk_extension and info.filename.endswith(compressed_apk_extension))): 252 is_compressed = compressed_extension and info.filename.endswith(compressed_apk_extension) 253 name = os.path.basename(info.filename) 254 if is_compressed: 255 name = name[:-len(compressed_extension)] 256 257 key = apk_key_map[name] 258 if key not in common.SPECIAL_CERT_STRINGS: 259 print " signing: %-*s (%s)" % (maxsize, name, key) 260 signed_data = SignApk(data, key, key_passwords[key], platform_api_level, 261 codename_to_api_level_map, is_compressed) 262 common.ZipWriteStr(output_tf_zip, out_info, signed_data) 263 else: 264 # an APK we're not supposed to sign. 265 print "NOT signing: %s" % (name,) 266 common.ZipWriteStr(output_tf_zip, out_info, data) 267 268 # System properties. 269 elif info.filename in ("SYSTEM/build.prop", 270 "VENDOR/build.prop", 271 "SYSTEM/etc/prop.default", 272 "BOOT/RAMDISK/prop.default", 273 "BOOT/RAMDISK/default.prop", # legacy 274 "ROOT/default.prop", # legacy 275 "RECOVERY/RAMDISK/prop.default", 276 "RECOVERY/RAMDISK/default.prop"): # legacy 277 print "rewriting %s:" % (info.filename,) 278 if stat.S_ISLNK(info.external_attr >> 16): 279 new_data = data 280 else: 281 new_data = RewriteProps(data, misc_info) 282 common.ZipWriteStr(output_tf_zip, out_info, new_data) 283 284 elif info.filename.endswith("mac_permissions.xml"): 285 print "rewriting %s with new keys." % (info.filename,) 286 new_data = ReplaceCerts(data) 287 common.ZipWriteStr(output_tf_zip, out_info, new_data) 288 289 # Ask add_img_to_target_files to rebuild the recovery patch if needed. 290 elif info.filename in ("SYSTEM/recovery-from-boot.p", 291 "SYSTEM/etc/recovery.img", 292 "SYSTEM/bin/install-recovery.sh"): 293 OPTIONS.rebuild_recovery = True 294 295 # Don't copy OTA keys if we're replacing them. 296 elif (OPTIONS.replace_ota_keys and 297 info.filename in ( 298 "BOOT/RAMDISK/res/keys", 299 "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem", 300 "RECOVERY/RAMDISK/res/keys", 301 "SYSTEM/etc/security/otacerts.zip", 302 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")): 303 pass 304 305 # Skip META/misc_info.txt since we will write back the new values later. 306 elif info.filename == "META/misc_info.txt": 307 pass 308 309 # Skip verity public key if we will replace it. 310 elif (OPTIONS.replace_verity_public_key and 311 info.filename in ("BOOT/RAMDISK/verity_key", 312 "ROOT/verity_key")): 313 pass 314 315 # Skip verity keyid (for system_root_image use) if we will replace it. 316 elif (OPTIONS.replace_verity_keyid and 317 info.filename == "BOOT/cmdline"): 318 pass 319 320 # Skip the care_map as we will regenerate the system/vendor images. 321 elif info.filename == "META/care_map.txt": 322 pass 323 324 # A non-APK file; copy it verbatim. 325 else: 326 common.ZipWriteStr(output_tf_zip, out_info, data) 327 328 if OPTIONS.replace_ota_keys: 329 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) 330 331 # Replace the keyid string in misc_info dict. 332 if OPTIONS.replace_verity_private_key: 333 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1]) 334 335 if OPTIONS.replace_verity_public_key: 336 if system_root_image: 337 dest = "ROOT/verity_key" 338 else: 339 dest = "BOOT/RAMDISK/verity_key" 340 # We are replacing the one in boot image only, since the one under 341 # recovery won't ever be needed. 342 ReplaceVerityPublicKey( 343 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1]) 344 345 # Replace the keyid string in BOOT/cmdline. 346 if OPTIONS.replace_verity_keyid: 347 ReplaceVerityKeyId(input_tf_zip, output_tf_zip, 348 OPTIONS.replace_verity_keyid[1]) 349 350 # Replace the AVB signing keys, if any. 351 ReplaceAvbSigningKeys(misc_info) 352 353 # Write back misc_info with the latest values. 354 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info) 355 356 357 def ReplaceCerts(data): 358 """Given a string of data, replace all occurences of a set 359 of X509 certs with a newer set of X509 certs and return 360 the updated data string.""" 361 for old, new in OPTIONS.key_map.iteritems(): 362 try: 363 if OPTIONS.verbose: 364 print " Replacing %s.x509.pem with %s.x509.pem" % (old, new) 365 f = open(old + ".x509.pem") 366 old_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower() 367 f.close() 368 f = open(new + ".x509.pem") 369 new_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower() 370 f.close() 371 # Only match entire certs. 372 pattern = "\\b"+old_cert16+"\\b" 373 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) 374 if OPTIONS.verbose: 375 print " Replaced %d occurence(s) of %s.x509.pem with " \ 376 "%s.x509.pem" % (num, old, new) 377 except IOError as e: 378 if e.errno == errno.ENOENT and not OPTIONS.verbose: 379 continue 380 381 print " Error accessing %s. %s. Skip replacing %s.x509.pem " \ 382 "with %s.x509.pem." % (e.filename, e.strerror, old, new) 383 384 return data 385 386 387 def EditTags(tags): 388 """Given a string containing comma-separated tags, apply the edits 389 specified in OPTIONS.tag_changes and return the updated string.""" 390 tags = set(tags.split(",")) 391 for ch in OPTIONS.tag_changes: 392 if ch[0] == "-": 393 tags.discard(ch[1:]) 394 elif ch[0] == "+": 395 tags.add(ch[1:]) 396 return ",".join(sorted(tags)) 397 398 399 def RewriteProps(data, misc_info): 400 output = [] 401 for line in data.split("\n"): 402 line = line.strip() 403 original_line = line 404 if line and line[0] != '#' and "=" in line: 405 key, value = line.split("=", 1) 406 if (key in ("ro.build.fingerprint", "ro.vendor.build.fingerprint") 407 and misc_info.get("oem_fingerprint_properties") is None): 408 pieces = value.split("/") 409 pieces[-1] = EditTags(pieces[-1]) 410 value = "/".join(pieces) 411 elif (key in ("ro.build.thumbprint", "ro.vendor.build.thumbprint") 412 and misc_info.get("oem_fingerprint_properties") is not None): 413 pieces = value.split("/") 414 pieces[-1] = EditTags(pieces[-1]) 415 value = "/".join(pieces) 416 elif key == "ro.bootimage.build.fingerprint": 417 pieces = value.split("/") 418 pieces[-1] = EditTags(pieces[-1]) 419 value = "/".join(pieces) 420 elif key == "ro.build.description": 421 pieces = value.split(" ") 422 assert len(pieces) == 5 423 pieces[-1] = EditTags(pieces[-1]) 424 value = " ".join(pieces) 425 elif key == "ro.build.tags": 426 value = EditTags(value) 427 elif key == "ro.build.display.id": 428 # change, eg, "JWR66N dev-keys" to "JWR66N" 429 value = value.split() 430 if len(value) > 1 and value[-1].endswith("-keys"): 431 value.pop() 432 value = " ".join(value) 433 line = key + "=" + value 434 if line != original_line: 435 print " replace: ", original_line 436 print " with: ", line 437 output.append(line) 438 return "\n".join(output) + "\n" 439 440 441 def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): 442 try: 443 keylist = input_tf_zip.read("META/otakeys.txt").split() 444 except KeyError: 445 raise common.ExternalError("can't read META/otakeys.txt from input") 446 447 extra_recovery_keys = misc_info.get("extra_recovery_keys", None) 448 if extra_recovery_keys: 449 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" 450 for k in extra_recovery_keys.split()] 451 if extra_recovery_keys: 452 print "extra recovery-only key(s): " + ", ".join(extra_recovery_keys) 453 else: 454 extra_recovery_keys = [] 455 456 mapped_keys = [] 457 for k in keylist: 458 m = re.match(r"^(.*)\.x509\.pem$", k) 459 if not m: 460 raise common.ExternalError( 461 "can't parse \"%s\" from META/otakeys.txt" % (k,)) 462 k = m.group(1) 463 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") 464 465 if mapped_keys: 466 print "using:\n ", "\n ".join(mapped_keys) 467 print "for OTA package verification" 468 else: 469 devkey = misc_info.get("default_system_dev_certificate", 470 "build/target/product/security/testkey") 471 mapped_keys.append( 472 OPTIONS.key_map.get(devkey, devkey) + ".x509.pem") 473 print("META/otakeys.txt has no keys; using %s for OTA package" 474 " verification." % (mapped_keys[0],)) 475 476 # recovery uses a version of the key that has been slightly 477 # predigested (by DumpPublicKey.java) and put in res/keys. 478 # extra_recovery_keys are used only in recovery. 479 cmd = ([OPTIONS.java_path] + OPTIONS.java_args + 480 ["-jar", 481 os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")] + 482 mapped_keys + extra_recovery_keys) 483 p = common.Run(cmd, stdout=subprocess.PIPE) 484 new_recovery_keys, _ = p.communicate() 485 if p.returncode != 0: 486 raise common.ExternalError("failed to run dumpkeys") 487 488 # system_root_image puts the recovery keys at BOOT/RAMDISK. 489 if misc_info.get("system_root_image") == "true": 490 recovery_keys_location = "BOOT/RAMDISK/res/keys" 491 else: 492 recovery_keys_location = "RECOVERY/RAMDISK/res/keys" 493 common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys) 494 495 # SystemUpdateActivity uses the x509.pem version of the keys, but 496 # put into a zipfile system/etc/security/otacerts.zip. 497 # We DO NOT include the extra_recovery_keys (if any) here. 498 499 temp_file = cStringIO.StringIO() 500 certs_zip = zipfile.ZipFile(temp_file, "w") 501 for k in mapped_keys: 502 common.ZipWrite(certs_zip, k) 503 common.ZipClose(certs_zip) 504 common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", 505 temp_file.getvalue()) 506 507 # For A/B devices, update the payload verification key. 508 if misc_info.get("ab_update") == "true": 509 # Unlike otacerts.zip that may contain multiple keys, we can only specify 510 # ONE payload verification key. 511 if len(mapped_keys) > 1: 512 print("\n WARNING: Found more than one OTA keys; Using the first one" 513 " as payload verification key.\n\n") 514 515 print "Using %s for payload verification." % (mapped_keys[0],) 516 cmd = common.Run( 517 ["openssl", "x509", "-pubkey", "-noout", "-in", mapped_keys[0]], 518 stdout=subprocess.PIPE) 519 pubkey, _ = cmd.communicate() 520 common.ZipWriteStr( 521 output_tf_zip, 522 "SYSTEM/etc/update_engine/update-payload-key.pub.pem", 523 pubkey) 524 common.ZipWriteStr( 525 output_tf_zip, 526 "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem", 527 pubkey) 528 529 return new_recovery_keys 530 531 532 def ReplaceVerityPublicKey(targetfile_zip, filename, key_path): 533 print "Replacing verity public key with %s" % (key_path,) 534 common.ZipWrite(targetfile_zip, key_path, arcname=filename) 535 536 537 def ReplaceVerityPrivateKey(misc_info, key_path): 538 print "Replacing verity private key with %s" % (key_path,) 539 misc_info["verity_key"] = key_path 540 541 542 def ReplaceVerityKeyId(targetfile_input_zip, targetfile_output_zip, keypath): 543 in_cmdline = targetfile_input_zip.read("BOOT/cmdline") 544 # copy in_cmdline to output_zip if veritykeyid is not present in in_cmdline 545 if "veritykeyid" not in in_cmdline: 546 common.ZipWriteStr(targetfile_output_zip, "BOOT/cmdline", in_cmdline) 547 return in_cmdline 548 out_cmdline = [] 549 for param in in_cmdline.split(): 550 if "veritykeyid" in param: 551 # extract keyid using openssl command 552 p = common.Run( 553 ["openssl", "x509", "-in", keypath, "-text"], 554 stdout=subprocess.PIPE) 555 keyid, stderr = p.communicate() 556 keyid = re.search( 557 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower() 558 print "Replacing verity keyid with %s error=%s" % (keyid, stderr) 559 out_cmdline.append("veritykeyid=id:%s" % (keyid,)) 560 else: 561 out_cmdline.append(param) 562 563 out_cmdline = ' '.join(out_cmdline) 564 out_cmdline = out_cmdline.strip() 565 print "out_cmdline %s" % (out_cmdline) 566 common.ZipWriteStr(targetfile_output_zip, "BOOT/cmdline", out_cmdline) 567 568 569 def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info): 570 """Replaces META/misc_info.txt. 571 572 Only writes back the ones in the original META/misc_info.txt. Because the 573 current in-memory dict contains additional items computed at runtime. 574 """ 575 misc_info_old = common.LoadDictionaryFromLines( 576 input_zip.read('META/misc_info.txt').split('\n')) 577 items = [] 578 for key in sorted(misc_info): 579 if key in misc_info_old: 580 items.append('%s=%s' % (key, misc_info[key])) 581 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items)) 582 583 584 def ReplaceAvbSigningKeys(misc_info): 585 """Replaces the AVB signing keys.""" 586 587 AVB_FOOTER_ARGS_BY_PARTITION = { 588 'boot' : 'avb_boot_add_hash_footer_args', 589 'dtbo' : 'avb_dtbo_add_hash_footer_args', 590 'system' : 'avb_system_add_hashtree_footer_args', 591 'vendor' : 'avb_vendor_add_hashtree_footer_args', 592 'vbmeta' : 'avb_vbmeta_args', 593 } 594 595 def ReplaceAvbPartitionSigningKey(partition): 596 key = OPTIONS.avb_keys.get(partition) 597 if not key: 598 return 599 600 algorithm = OPTIONS.avb_algorithms.get(partition) 601 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,) 602 603 print 'Replacing AVB signing key for %s with "%s" (%s)' % ( 604 partition, key, algorithm) 605 misc_info['avb_' + partition + '_algorithm'] = algorithm 606 misc_info['avb_' + partition + '_key_path'] = key 607 608 extra_args = OPTIONS.avb_extra_args.get(partition) 609 if extra_args: 610 print 'Setting extra AVB signing args for %s to "%s"' % ( 611 partition, extra_args) 612 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition] 613 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args) 614 615 for partition in AVB_FOOTER_ARGS_BY_PARTITION: 616 ReplaceAvbPartitionSigningKey(partition) 617 618 619 def BuildKeyMap(misc_info, key_mapping_options): 620 for s, d in key_mapping_options: 621 if s is None: # -d option 622 devkey = misc_info.get("default_system_dev_certificate", 623 "build/target/product/security/testkey") 624 devkeydir = os.path.dirname(devkey) 625 626 OPTIONS.key_map.update({ 627 devkeydir + "/testkey": d + "/releasekey", 628 devkeydir + "/devkey": d + "/releasekey", 629 devkeydir + "/media": d + "/media", 630 devkeydir + "/shared": d + "/shared", 631 devkeydir + "/platform": d + "/platform", 632 }) 633 else: 634 OPTIONS.key_map[s] = d 635 636 637 def GetApiLevelAndCodename(input_tf_zip): 638 data = input_tf_zip.read("SYSTEM/build.prop") 639 api_level = None 640 codename = None 641 for line in data.split("\n"): 642 line = line.strip() 643 if line and line[0] != '#' and "=" in line: 644 key, value = line.split("=", 1) 645 key = key.strip() 646 if key == "ro.build.version.sdk": 647 api_level = int(value.strip()) 648 elif key == "ro.build.version.codename": 649 codename = value.strip() 650 651 if api_level is None: 652 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") 653 if codename is None: 654 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop") 655 656 return (api_level, codename) 657 658 659 def GetCodenameToApiLevelMap(input_tf_zip): 660 data = input_tf_zip.read("SYSTEM/build.prop") 661 api_level = None 662 codenames = None 663 for line in data.split("\n"): 664 line = line.strip() 665 if line and line[0] != '#' and "=" in line: 666 key, value = line.split("=", 1) 667 key = key.strip() 668 if key == "ro.build.version.sdk": 669 api_level = int(value.strip()) 670 elif key == "ro.build.version.all_codenames": 671 codenames = value.strip().split(",") 672 673 if api_level is None: 674 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") 675 if codenames is None: 676 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop") 677 678 result = dict() 679 for codename in codenames: 680 codename = codename.strip() 681 if len(codename) > 0: 682 result[codename] = api_level 683 return result 684 685 686 def main(argv): 687 688 key_mapping_options = [] 689 690 def option_handler(o, a): 691 if o in ("-e", "--extra_apks"): 692 names, key = a.split("=") 693 names = names.split(",") 694 for n in names: 695 OPTIONS.extra_apks[n] = key 696 elif o in ("-d", "--default_key_mappings"): 697 key_mapping_options.append((None, a)) 698 elif o in ("-k", "--key_mapping"): 699 key_mapping_options.append(a.split("=", 1)) 700 elif o in ("-o", "--replace_ota_keys"): 701 OPTIONS.replace_ota_keys = True 702 elif o in ("-t", "--tag_changes"): 703 new = [] 704 for i in a.split(","): 705 i = i.strip() 706 if not i or i[0] not in "-+": 707 raise ValueError("Bad tag change '%s'" % (i,)) 708 new.append(i[0] + i[1:].strip()) 709 OPTIONS.tag_changes = tuple(new) 710 elif o == "--replace_verity_public_key": 711 OPTIONS.replace_verity_public_key = (True, a) 712 elif o == "--replace_verity_private_key": 713 OPTIONS.replace_verity_private_key = (True, a) 714 elif o == "--replace_verity_keyid": 715 OPTIONS.replace_verity_keyid = (True, a) 716 elif o == "--avb_vbmeta_key": 717 OPTIONS.avb_keys['vbmeta'] = a 718 elif o == "--avb_vbmeta_algorithm": 719 OPTIONS.avb_algorithms['vbmeta'] = a 720 elif o == "--avb_vbmeta_extra_args": 721 OPTIONS.avb_extra_args['vbmeta'] = a 722 elif o == "--avb_boot_key": 723 OPTIONS.avb_keys['boot'] = a 724 elif o == "--avb_boot_algorithm": 725 OPTIONS.avb_algorithms['boot'] = a 726 elif o == "--avb_boot_extra_args": 727 OPTIONS.avb_extra_args['boot'] = a 728 elif o == "--avb_dtbo_key": 729 OPTIONS.avb_keys['dtbo'] = a 730 elif o == "--avb_dtbo_algorithm": 731 OPTIONS.avb_algorithms['dtbo'] = a 732 elif o == "--avb_dtbo_extra_args": 733 OPTIONS.avb_extra_args['dtbo'] = a 734 elif o == "--avb_system_key": 735 OPTIONS.avb_keys['system'] = a 736 elif o == "--avb_system_algorithm": 737 OPTIONS.avb_algorithms['system'] = a 738 elif o == "--avb_system_extra_args": 739 OPTIONS.avb_extra_args['system'] = a 740 elif o == "--avb_vendor_key": 741 OPTIONS.avb_keys['vendor'] = a 742 elif o == "--avb_vendor_algorithm": 743 OPTIONS.avb_algorithms['vendor'] = a 744 elif o == "--avb_vendor_extra_args": 745 OPTIONS.avb_extra_args['vendor'] = a 746 else: 747 return False 748 return True 749 750 args = common.ParseOptions( 751 argv, __doc__, 752 extra_opts="e:d:k:ot:", 753 extra_long_opts=[ 754 "extra_apks=", 755 "default_key_mappings=", 756 "key_mapping=", 757 "replace_ota_keys", 758 "tag_changes=", 759 "replace_verity_public_key=", 760 "replace_verity_private_key=", 761 "replace_verity_keyid=", 762 "avb_vbmeta_algorithm=", 763 "avb_vbmeta_key=", 764 "avb_vbmeta_extra_args=", 765 "avb_boot_algorithm=", 766 "avb_boot_key=", 767 "avb_boot_extra_args=", 768 "avb_dtbo_algorithm=", 769 "avb_dtbo_key=", 770 "avb_dtbo_extra_args=", 771 "avb_system_algorithm=", 772 "avb_system_key=", 773 "avb_system_extra_args=", 774 "avb_vendor_algorithm=", 775 "avb_vendor_key=", 776 "avb_vendor_extra_args=", 777 ], 778 extra_option_handler=option_handler) 779 780 if len(args) != 2: 781 common.Usage(__doc__) 782 sys.exit(1) 783 784 input_zip = zipfile.ZipFile(args[0], "r") 785 output_zip = zipfile.ZipFile(args[1], "w", 786 compression=zipfile.ZIP_DEFLATED, 787 allowZip64=True) 788 789 misc_info = common.LoadInfoDict(input_zip) 790 791 BuildKeyMap(misc_info, key_mapping_options) 792 793 certmap, compressed_extension = common.ReadApkCerts(input_zip) 794 apk_key_map = GetApkCerts(certmap) 795 CheckAllApksSigned(input_zip, apk_key_map, compressed_extension) 796 797 key_passwords = common.GetKeyPasswords(set(apk_key_map.values())) 798 platform_api_level, _ = GetApiLevelAndCodename(input_zip) 799 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip) 800 801 ProcessTargetFiles(input_zip, output_zip, misc_info, 802 apk_key_map, key_passwords, 803 platform_api_level, 804 codename_to_api_level_map, 805 compressed_extension) 806 807 common.ZipClose(input_zip) 808 common.ZipClose(output_zip) 809 810 # Skip building userdata.img and cache.img when signing the target files. 811 new_args = ["--is_signing"] 812 # add_img_to_target_files builds the system image from scratch, so the 813 # recovery patch is guaranteed to be regenerated there. 814 if OPTIONS.rebuild_recovery: 815 new_args.append("--rebuild_recovery") 816 new_args.append(args[1]) 817 add_img_to_target_files.main(new_args) 818 819 print "done." 820 821 822 if __name__ == '__main__': 823 try: 824 main(sys.argv[1:]) 825 except common.ExternalError, e: 826 print 827 print " ERROR: %s" % (e,) 828 print 829 sys.exit(1) 830 finally: 831 common.Cleanup() 832